From 729eeac045d189f5daef0a5d4780397ad09ac366 Mon Sep 17 00:00:00 2001 From: Denozordec Date: Sun, 8 Nov 2026 02:57:43 +0700 Subject: [PATCH] =?UTF-8?q?=D0=A1=D0=B5=D0=B3=D0=BC=D0=B5=D0=BD=D1=82?= =?UTF-8?q?=D0=B0=D1=82=D0=BE=D1=80:=20scene-=D0=B8=D0=BC=D1=8F=20=D1=80?= =?UTF-8?q?=D0=B5=D0=BB=D0=B8=D0=B7=D0=B0=20=D0=BD=D0=B5=20=D0=BC=D0=BE?= =?UTF-8?q?=D0=B6=D0=B5=D1=82=20=D0=B1=D1=8B=D1=82=D1=8C=20=D0=B4=D0=BE?= =?UTF-8?q?=D1=80=D0=BE=D0=B6=D0=BA=D0=BE=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Kinozal 2160145: после переобучения модель выбирала первую строку вкладки «Релиз» («Memoirs.of.a.Geisha.2005.2160p.UHD.Blu-ray.Remux…-CiNEPHiLES», оценка 0.96), и имя релиза попадало в ответ как «студия». Прежние веса строку не выбирали. - html_extract.RE_SCENE: без пробелов и ≥ 4 точек — дорожкой не бывает; LineSegmenter.select_lines отбрасывает такие строки при любой оценке модели. - build_examples: scene-имена — трудные негативы, берутся все (раньше прореживались до 15 %); эффект — при следующем переобучении. - На реальных данных страницы (вкладки Техданные/Интересно/Награды/Релиз): 1 дорожка «проф. дубляж», без студии. Тесты: 288 ok. Co-Authored-By: Claude Sonnet 5.5 --- src/html_extract.py | 3 +++ src/segmenter.py | 8 ++++---- tests/run_tests.py | 12 +++++++++++- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/html_extract.py b/src/html_extract.py index 257da2a..bbf7226 100644 --- a/src/html_extract.py +++ b/src/html_extract.py @@ -51,6 +51,9 @@ RE_SEP = re.compile( RE_TYPE_START = re.compile( r"^\s*(?:Профессиональн|Любительск|Авторск|Дублирован|Многоголос|Двухголос|Одноголос)", re.I) +# scene-имя релиза «A.b.c.2005.2160p.UHD.Blu-ray.Remux-GROUP»: без пробелов, ≥ 4 точек; дорожкой не бывает +RE_SCENE = re.compile(r"^\S+(?:\.\S+){4,}$") + SELECTORS = ("div.post_body", "div[id^=post-text-]", ".postbody", "#tabs") diff --git a/src/segmenter.py b/src/segmenter.py index e1cef25..38d7612 100644 --- a/src/segmenter.py +++ b/src/segmenter.py @@ -34,7 +34,7 @@ import numpy as np import torch from transformers import AutoModelForSequenceClassification, AutoTokenizer -from .html_extract import read_input +from .html_extract import RE_SCENE, read_input BACKBONE = "cointegrated/rubert-tiny2" MAX_LEN = 160 @@ -189,8 +189,8 @@ def build_examples(split: str, rng: random.Random, synth_per_page: int = 6) -> l continue # неразмеченная кодек-строка: ни позитив, ни негатив prev = lines[i - 1] if i else "" nxt = lines[i + 1] if i + 1 < len(lines) else "" - if not y and not HINT.search(line) and rng.random() > 0.15: - continue # простые негативы прореживаем, трудные (с намёками) берём все + if not y and not HINT.search(line) and not RE_SCENE.match(line) and rng.random() > 0.15: + continue # простые негативы прореживаем, трудные (с намёками, scene-имена релизов) берём все ex.append((*make_input(prev, line, nxt), y)) if not want_test: negs = [l for l, y in zip(lines, labels) if y == 0] @@ -231,7 +231,7 @@ class LineSegmenter: if not lines: return [] probs = self.score(lines) - return [l for l, p in zip(lines, probs) if p >= threshold] + return [l for l, p in zip(lines, probs) if p >= threshold and not RE_SCENE.match(l)] # ---------------------------------------------------------------- обучение diff --git a/tests/run_tests.py b/tests/run_tests.py index 19fff3b..9ee7d5d 100644 --- a/tests/run_tests.py +++ b/tests/run_tests.py @@ -16,7 +16,7 @@ from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parents[1])) -from src.html_extract import (RE_LINE_PREFIX, _split_entries, # noqa: E402 +from src.html_extract import (RE_LINE_PREFIX, RE_SCENE, _split_entries, # noqa: E402 extract_segments, html_to_text) from src.studio_norm import canonical, find_studio_span # noqa: E402 @@ -151,6 +151,16 @@ check("предупреждение: модель пуста, правила н check("предупреждение: правила нашли больше", "больше" in (segmenter_warning(True, "model", ["a"], ["a", "b"]) or "")) check("без предупреждения: совпало / нет модели", segmenter_warning(True, "model", ["a"], ["a"]) is None and segmenter_warning(False, "rules", [], ["x"]) is None) +check("scene-имя релиза — не дорожка (kinozal 2160145)", + RE_SCENE.match("Memoirs.of.a.Geisha.2005.2160p.UHD.Blu-ray.Remux.DV.HDR.HEVC.TrueHD.Atmos.7.1-CiNEPHiLES") + and not RE_SCENE.match("Профессиональный (многоголосый закадровый) | HDRezka Studio") + and not RE_SCENE.match("Аудио 1: Русский (DTS-HD MA, 6 ch, 2240 Кбит/с) / Дублированный")) +from src.segmenter import LineSegmenter # noqa: E402 +import numpy as _np # noqa: E402 +_seg = object.__new__(LineSegmenter) +_seg.score = lambda lines: _np.ones(len(lines)) # модель «выбирает» всё +_got = _seg.select_lines("Перевод: Дублированный\nMemoirs.of.a.Geisha.2005.2160p.UHD.Remux-GRP\nАудио 1: Русский / Дублированный") +check("select_lines отбрасывает scene-имя даже при оценке 1.0", len(_got) == 2 and all("Geisha" not in x for x in _got), str(_got)) # ---------------------------------------------------------------- html print("== html_to_text ==")