- Регистрация
- 13.08.2012
- Сообщения
- 840
- Реакции
- 588
- Баллы
- 93
Бот, который учится сам: учим ZennoPoster видеть как человек!
- Мир элементов: кнопки, поля, ссылки. ZennoPoster открывает структуру документа (DOM) и делает с ними что хочет: находит, кликает, читает, ждёт.
- Мир пикселей: картинки,
canvas, флеш, встроенные плееры, капчи и прочие нарисованные интерфейсы.
Эта статья — на конкурс, и она не про то, «как разгадывать капчу» (для этого есть платные API и индусы). Она про фундаментальный принцип, который у меня вырос из этой задачи: как научить ZennoPoster смотреть на экран, принимать решения по картинке и главное, самому учиться на собственной работе. Капчу я взял специально как самый простой и понятный полигон: на ней нет ничего лишнего — экран, объект, клик, правильный ответ. Если принцип объясняется на ней, он ляжет и на любую другую задачу. Код под статьёй открыт, платных сервисов в схеме нет, а бюджет проекта — ноль рублей.
Каждый, кто автоматизирует сложные сайты, натыкался на тупик, когда опускаются руки:
- Кнопка нарисована внутри флеш-виджета — элемента в дереве нет.
- Цена товара отрисована картинкой.
- Прогресс загрузки крутится в
canvas— никакой запрос к документу не скажет, когда он закончится. - Интерфейс встроенного плеера не имеет ни одного доступного элемента.
Система, которая «видит и учится», у меня держится на трех китах:
- Глаза (Детектор объектов YOLO)
Обученная нейросеть получает скриншот и отвечает: вот объект, вот рамка, вот уверенность. - Учитель (Источник правильных ответов)
В каждой задаче есть источник правды. Иногда это человек, который размечает сложные случаи. Иногда — сама жизнь: клик сработал и что-то произошло — значит, пример правильный; ни черта не произошло — нейронка ошиблась. - Память (Датасет)
Правильные ответы не выбрасываются в корзину, а бережно копятся в датасет. Когда набирается порог, модель дообучается. Чем дольше бот живёт, тем реже нужен человек и тем ниже ваши затраты.
Приём «найди объект по картинке и кликни» — это не узкая специализация, а универсальный контур, под который ложится почти любая сложная задача:
- Кнопки там, где нет элементов
Canvas-приложения, старые флеш-виджеты, Unity-игры в окне браузера, плееры с собственным интерфейсом. YOLO учится находить их по скриншоту, дальше бот кликает по центру найденной рамки. Если дизайн изменится, самообучение подтянет модель новыми примерами, и ваши руки ничего переделывать не будут. - Cookie-баннеры и поп-апы
Формы, которые сайты меняют каждый месяц. Сегодня у баннера кнопка «Принять» справа, через месяц сайт перекрасится, и она окажется в другом месте. Размечать такое вручную на каждом сайте — унылое занятие. А если натренировать модель на кнопках согласия и дать ей учителя в лице исчезновения баннера после клика, она сама подтянется под новые дизайны маркетологов. - Состояния, которых нет в DOM
Загрузки, спиннеры, прогресс-бары в видеоплеерах, появление модалки по анимации. Там, где структура документа не совпадает с картинкой, удобнее спрашивать сам экран: дождаться, когда спиннер наконец-то исчезнет, или убедиться, что рекламный блок реально отрисовался, а не остался белым квадратом. - То, что ZennoPoster проверять вообще не умеет
Допустим, нужно отсортировать фотографии: одни подходят под фильтр, другие нет. Структура документа тут бессильна — это же просто пиксели. А детектор, обученный на паре классов, умеет. Учителем выступает человек, который размечает первые сотни картинок, или ответ модерации на результат. Туда же: найти QR-код на упаковке, отличить картинку от иконки, поймать логотип не на своём месте.
Разумный вопрос: почему бы не собрать датасет в начале, один раз отмучиться с обучением модели и забыть? Потому что нарисованный мир динамичен и жесток. Сайт перекрасился, флеш заменили на canvas, капча получила новый набор картинок, баннер «слегка поехал» из-за кривой верстки. Модель, обученная один раз, к этим переменам глуха, и скоро всё придётся переделывать руками.
Самообучение закрывает это автоматически: данные собираются прямо во время боевой работы, и модель живёт на актуальных картинках. Правило простое — не выбрасывать ничего. Уверенный ответ — пример. Человек помог — ещё пример. Ответ «объекта нет» — тоже пример, пусть и отрицательный. Всё это копится и каждые N примеров перегоняется в новую версию модели, делая бота умнее.
Вся работа архитектуры развернута вокруг локальной папки
C:\YOLO и состоит из четырех базовых элементов:Файл / Папка/ Роль в системе
yolo_final.pyИнференс: принимает скриншот, возвращает координаты рамок объектов и уверенностьyolo_train.pyСкрипт дообучения модели на новых накопившихся примерах.train_params.jsonВременный конфигуратор параметров обучения (динамически создается ботом).C# Логика ботаСнятие скриншотов, клики, интерфейс ручной разметки, логика доверия (пороги).images/ и labels/Хранилище боевого датасета (картинки и их текстовые .txt двойники с разметкой)backup/Папка для хранения 5 последних версий моделей (best.pt) на случай отката.Рядом живут ещё два служебных файла:
train_progress.txt (текущая эпоха и loss, пока идёт обучение) и train.lock (замок, чтобы два экземпляра бота не устроили драку за ресурсы и не запустили обучение одновременно). Они, как и train_params.json, временные: появляются на время обучения и убираются после.Показываю на примере reCAPTCHA v2 (сетка 3×3). И взял её не потому, что она главная, а потому, что проще не бывает: никаких абстракций, вся подноготная на тарелке. На верхнем уровне логика циркулирует следующим образом:
Код:
[ Скриншот сетки 3x3 ] ➔ [ Детекция YOLO ] ➔ [ Анализ порогов уверенности ]
│
┌───────────────────────┴───────────────────────┐
▼ ▼
(Уверенность > 0.35) (Уверенность 0.15 - 0.35)
│ │
▼ ▼
[ Авто-клик бота ] [ Окно разметки руками ]
│ │
└───────────────────────┬───────────────────────┘
▼
[ Сохранение в датасет ]
│
▼
(Накопилось 100 новых кадров?)
│
▼
[ Фоновое обучение yolo_train.py ]
Шаг 1. Снять капчу так, чтобы её можно было скормить нейронке
Скриншот всей страницы не годится: там куча шума, YOLO будет отвлекаться на посторонние элементы и ловить галлюцинации. Действие «сохранение капчи» делает срез аккуратно:
- Ищет
iframeсо словомbframeвsrc— это окно капчи, — и ждёт чтобы страница успокоилась после анимации. - Режет кадр через
ImageProcessingCropFromScreenshot, отрезая сверху «шапку» с заданием (для широкой капчи отступ 130 пикселей, для узкой — 22% высоты, плюс ещё 10, чтобы синяя полоска гарантированно не влезла в кадр). - Высота квадрата — ширина минус 10 пикселей: голая эмпирика, но низ сетки не уезжает.
Шаг 2. «Выберите автобусы» превращается в номер класса
Задание приходит текстом — «select all images with buses», — а нейронке нужен номер класса, буквы она не понимает. Переводят две функции:
NormalizeTaskчистит текст от артиклей и костылями пытается свести множественное число к единственному (buses➔bus). Здесь важно не накосячить: если регулярка или костыль сработает криво, робот начнет искать автобусы вместо пожарных гидрантов.- Затем
GetClassIdForTaskищет слово в общем словаре. Словарь один для C# и Python, индексы жёстко совпадают сdata.yaml: всего 10 уникальных индексов, от 0 (bicycle) до 10 (traffic light). Если перепутать индексыcarиbusна этом этапе, капча будет решаться уверенно, но абсолютно неправильно, сводя с ума лог бота.
Если слово вообще не нашлось в словаре — возвращаем
classId = -1. Но картинку мы не выбрасываем! Она уходит в датасет с префиксом файла bg_. YOLO должна знать врага в лицо и понимать, как выглядит экран, когда на нём вообще ничего нужного нет. Для нейросети такое фоновое изображение — это обычная картинка в папке images/, для которой в папке labels/ создается абсолютно пустой текстовый файл (без единой строчки координат). Если скармливать модели только «успешные» картинки, где точно есть автобусы, она начнет видеть их даже в чистом поле. Пустые кадры лечат эту слепоту.Шаг 3. Логика трех порогов уверенности
Дальше запускается
yolo_final.py: картинка на вход, список рамок с уверенностью на выход. Решение принимается по трём порогам:- Уверенность выше 0.35 — бот считает себя гением и кликает сам.
- От 0.15 до 0.35 — «почти уверен», нейронка сомневается, поэтому на всякий случай зовётся кожаный мешок.
- Ниже 0.15 — считаем, что объекта на кадре нет.
Шаг 4. Клики по найденным клеткам
Рамки из YOLO — это пиксели. Функция переводит их в номер клетки: шаг сетки и размер ячейки берутся из параметров, клик всегда идёт строго в геометрический центр. Если объект не влез в клетку целиком — не страшно: рамка его поймала, а кликается вся ячейка.
Отдельная функция «клик по клетке» защищает от глупости: массив
clickedCells[3,3] запоминает уже кликнутые клетки, чтобы робот не зацикливался и не тыкал дважды в одно место, если нейронка нашла там два разных микрообъекта. Плюс зашит обязательный Sleep для ожидания перерисовки — у капчи она идет с задержкой, и быстрый бот просто закликает пустоту. Скучный, но необходимый быт автоматизации.Шаг 5. Когда на помощь зовут человека
Самое честное место статьи. Нейросеть, какой бы крутой она ни была, не умеет всего сразу: в первые дни эксплуатации она ведёт себя как туповатый стажёр, который бегает к начальнику по каждому чиху. Поэтому в проекте есть маленькое окошко — появляется, когда ни одна рамка не дотянула до уверенности 0.35.
Окошко показывает девять увеличенных клеток, человек просто лениво тыкает в те, где объект реально есть. Разметка улетает в датасет, клики на сайте бот повторяет сам. Человек устаёт меньше, потому что его зовут только когда бот реально сомневается. Первые дни это похоже на проверку домашнего задания у первоклассника, а через неделю — на редкий перерыв на кофе раз в день.
Шаг 6. Автосбор спорных кадров и защита от раздувания
Кадры, на которых бот позвал человека, — золото в буквальном смысле: именно на них модель чаще всего косячит, и именно на них её нужно учить. Поэтому в датасет попадает всё подряд: спорная разметка, уверенные ответы и пустые кадры.
Здесь есть важная засада: перед записью каждая картинка сверяется с тем, что уже есть в базе — строго байт в байт (через хэширование или прямое сравнение массивов байт), а не по имени файла. Если этого не сделать, хранилище мгновенно разрастётся мегабайтами дубликатов, а пользы от обучения на одинаковых картинках будет ровно ноль. Также зафиксирован приоритет: ручная разметка всегда сильнее автоматической. Модель никогда не перезапишет файлы, которые уже разметил человек, иначе весь ручной труд пойдет коту под хвост.
Когда заходит речь о нейросетях, все сразу представляют фермы из видеокарт и счета за электричество. Давайте спустим математику на землю.
Мы используем легкие версии YOLO (например, YOLOv26). Они изначально проектировались для работы на смартфонах и умных камерах, поэтому ресурсы они буквально «нюхают».
- Инференс (Распознавание кадра через
yolo_final.py): Занимает около 150 миллисекунд. Спокойно работает на обычном процессоре (CPU) на любом копеечном VPS. Видеокарта боту в процессе работы на сайтах не нужна вообще. - Дообучение (
yolo_train.py): Вот здесь процессору становится тяжело. Обучение 20 эпох на сотне картинок на CPU может занять 15–20 минут жесткого загруза системы. На видеокарте Nvidia (с поддержкой CUDA) этот процесс пролетает за 30–40 секунд.
Бот работает на обычном дешевом CPU-сервере. Когда накапливается 100 новых картинок, срабатывает триггер обучения. Процесс запускается параллельно, с низким приоритетом (low priority) (низкий приоритет процесса в ОС). Пока Python на фоне не спеша шуршит по ядрам процессора и собирает новую модель, ZennoPoster продолжает крутить потоки на старой модели
best.pt. Как только скрипт обучения закончил работу — он молча перезаписывает файл модели, и C# подхватывает его на следующем цикле. Никаких тормозов интерфейса или зависания потоков.Формат разметки для YOLO — с виду мелкая текстовая чепуха, а без неё система превращается в тыкву. Внутри
.txt файлов разметки лежат строки вида:класс центр_по_x центр_по_y ширина высотаНормировка строго от 0 до 1 относительно размера картинки (например:
0 0.512 0.344 0.120 0.150). И вот тут кроется самая дорогая и глупая ошибка, на которой я потерял неделю жизни: локаль операционной системы.Если у вас на сервере стоит русская локаль Windows, C# при выводе дробных координат в файл тихо и незаметно заменит точку на запятую (
0 0,512 0,344...). YOLO от такого формата молча сходит с ума. Она не выдает ошибок в консоль, она просто считает эти строки мусором, обучение улетает в космос, а итоговая модель становится абсолютно слепой и бесполезной. Поэтому CultureInfo.InvariantCulture должно быть жестко прописано в каждом месте C#-кода, где числа переводятся в текст для сохранения файлов разметки!Не буду приукрашивать и включать инфоцыгана: нейросеть — это не магия и не искусственный разум, это просто суровая математика и механика. Быстрая часть — написать код и сделать стартовую разметку. Долгая — отладить систему так, чтобы человек требовался всё реже. По моим меркам, уже через пару дней эксплуатации доля ручных вмешательств уверенно падает на дно: бот сам набивает датасет, и точность подтягивается автоматически.
Капча — это на самом деле просто наглядная игрушка, и взял я её только потому, что на ней этот принцип виден лучше всего: экран, объект, клик, правильный ответ. Писать сказки в стиле «моя рекапча теперь всегда решается со 100% успехом» я не буду, Google не дурак и делает её неприятной специально, подкидывая новые подлянки. Но смысл статьи вообще не в ней.
Чтобы вы не решили, что всё это — просто про картиночки с автобусами и гидрантами: тот же контур у меня успешно работал на задачах в разы серьезнее и дороже. С помощью этого подхода я автоматизировал получение регистраций на визы и проходил верификацию по телефону в режиме живого видео — связка «аккаунт, реальное лицо на фронталке и сама верификация» шла одним автоматическим сценарием. Там уже были не квадратные клетки сетки, а живые динамические экраны и настоящий строгий оператор на том конце. Механика один в один: снял кадр, YOLO приняла решение по картинке, бот сделал действие, забрал правильный ответ. Ручками на каждого человека уходили драгоценные минуты, выделенные на звонок и верификацию — с ботом же это превратилось в рядовой сценарий.
Смысл статьи ровно в этом: у ZennoPoster есть ещё один, огромный уровень возможностей, который лежит за пределами привычного дерева элементов. Научите своего бота понимать, что нарисовано на экране, дайте ему память, и пусть он сам учится на собственных косяках. Соберите свою коробку «глаза + учитель + память» и автоматизируйте то, к чему раньше боялись подступиться.
Чтобы вся эта связка из ZennoPoster, Python и YOLO завелась на вашем сервере или домашнем ПК, нужно развернуть минимальное окружение. Бюджет, как и договаривались, — ровно ноль рублей. Сложного ничего нет, справится даже тот, кто Python видел только на картинках.
1. Ставим Python
Идём на официальный сайт и скачиваем Python 3.10 или 3.11. Более свежие версии (3.12 и выше) пока не трогаем — у нейросетевых библиотек с ними бывают свои капризы и тёрки, нам лишняя головная боль не нужна.
Самый важный нюанс: когда запустите установщик, на первом же экране ОБЯЗАТЕЛЬНО отметьте галочку «Add Python to PATH». Если этого не сделать, ZennoPoster просто не сможет достучаться до скриптов, и система превратится в тыкву.
2. Скармливаем библиотеки (Зависимости)
Открываем стандартную командную строку Windows (cmd), копируем туда одну-единственную команду и жмём Enter:
Что мы сейчас установили:
Тут два пути, выбирайте свой в зависимости от кошелька и железа:
1. Ставим Python
Идём на официальный сайт и скачиваем Python 3.10 или 3.11. Более свежие версии (3.12 и выше) пока не трогаем — у нейросетевых библиотек с ними бывают свои капризы и тёрки, нам лишняя головная боль не нужна.
Самый важный нюанс: когда запустите установщик, на первом же экране ОБЯЗАТЕЛЬНО отметьте галочку «Add Python to PATH». Если этого не сделать, ZennoPoster просто не сможет достучаться до скриптов, и система превратится в тыкву.
2. Скармливаем библиотеки (Зависимости)
Открываем стандартную командную строку Windows (cmd), копируем туда одну-единственную команду и жмём Enter:
Код:
pip install ultralytics opencv-python pillow
Код:
Алярм! Что делать, если консоль шлёт лесом и пишет: «"pip" не является внутренней или внешней командой»?Спокойно, без паники — это классический косяк новичка. Значит, вы всё-таки умудрились проглядеть ту самую галочку «Add Python to PATH», когда ставили Питон.
Сносить всё и переустанавливать не надо, есть хитрый обходной маневр. Заставляем систему вызвать установщик силой через сам Питон. Вбиваем в консоль вот так: python -m pip install ultralytics opencv-python pillow
Если консоль опять ушла в отказ и начала ныть, что и python она тоже не знает — значит, винда вообще в упор не видит, куда вы там его установили.
В таком случае просто кликаем дважды по скачанному файлу установщика Питона, жмём кнопку Modify (Изменить) и на следующем шаге с чувством выполненного долга наконец-то втыкаем эту многострадальную галочку Add Python to environment variables.
После этого перезапускаем консоль, и всё взлетит как миленькое!
- ultralytics — это официальный, суровый «движок» от создателей YOLO. Вообще он умеет сам качать базовую микро-модель при первом старте, но чтобы вы не страдали, я уже закинул её в шаблон "yolo26s.pt".
opencv-python и pillow — наши рабочие лошадки. Они нужны исключительно для того, чтобы Python умел мгновенно кропать кадры, читать пиксели картинок и не спотыкаться на лету.
Тут два пути, выбирайте свой в зависимости от кошелька и железа:
- Вариант «Нищеброд-эдишн» (Обычный CPU-сервер): Если бот крутится на копеечном VPS без видеокарты — выдыхайте, делать больше ничего не надо. Скрипт обучения умный: он сам поймет, что CUDA на борту нет, и начнет тихонько шуршать по ядрам процессора на низком приоритете, вообще не мешая основным потокам зенки.
Вариант «Мажорный» (Видяха от Nvidia):Если у вас дома или на сервере стоит нормальная карта от «зелёных» и вы хотите, чтобы дообучение пролетало за 30 секунд, а не за 20 минут, делаем три шага:- Обновляем драйверы видеокарты до упора.
Скачиваем и накатываем CUDA Toolkit ( рекомендую версии 11.8 или 12.1). - Заходим на сайт pytorch.org, в их конфигураторе выбираем свою версию CUDA, копируем сгенерированную команду и выполняем её в консоли. Она заменит стандартную CPU-версию пакетов на быструю графическую.
- Обновляем драйверы видеокарты до упора.
Проблема: Локаль Windows ломает пути, если в них есть кириллицаСкрипты Python падают с дикими ошибками UnicodeDecodeError или SyntaxError где-то внутри недр библиотек.
В чем прикол: Ошибка возникает, если папка проекта лежит по пути, содержащему русские буквы (например, C:\Users\Иван\Desktop\YOLO). Библиотека ultralytics и внутренние сишные модули Python очень плохо переваривают кириллицу в путях к датасету и файлам конфигурации data.yaml.
Как лечить: В статье не зря написано: вся работа развернута вокруг локальной папки C:\YOLO. Не изобретайте велосипед, не кладите проект на рабочий стол или в «Мои документы». Путь должен состоять исключительно из латинских букв и быть как можно короче.
В чем прикол: Ошибка возникает, если папка проекта лежит по пути, содержащему русские буквы (например, C:\Users\Иван\Desktop\YOLO). Библиотека ultralytics и внутренние сишные модули Python очень плохо переваривают кириллицу в путях к датасету и файлам конфигурации data.yaml.
Как лечить: В статье не зря написано: вся работа развернута вокруг локальной папки C:\YOLO. Не изобретайте велосипед, не кладите проект на рабочий стол или в «Мои документы». Путь должен состоять исключительно из латинских букв и быть как можно короче.
Вложения
Последнее редактирование:


