惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Schneier on Security
F
Fortinet All Blogs
B
Blog
GbyAI
GbyAI
P
Proofpoint News Feed
量子位
The Register - Security
The Register - Security
宝玉的分享
宝玉的分享
大猫的无限游戏
大猫的无限游戏
云风的 BLOG
云风的 BLOG
V
Visual Studio Blog
B
Blog RSS Feed
WordPress大学
WordPress大学
Recorded Future
Recorded Future
Recent Announcements
Recent Announcements
V
Vulnerabilities – Threatpost
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
S
Secure Thoughts
雷峰网
雷峰网
Stack Overflow Blog
Stack Overflow Blog
C
Cybersecurity and Infrastructure Security Agency CISA
Webroot Blog
Webroot Blog
AWS News Blog
AWS News Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
The GitHub Blog
The GitHub Blog
爱范儿
爱范儿
O
OpenAI News
月光博客
月光博客
H
Hacker News: Front Page
S
Security Affairs
W
WeLiveSecurity
The Hacker News
The Hacker News
aimingoo的专栏
aimingoo的专栏
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Help Net Security
Help Net Security
MongoDB | Blog
MongoDB | Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
D
Docker
T
The Blog of Author Tim Ferriss
Spread Privacy
Spread Privacy
Blog — PlanetScale
Blog — PlanetScale
J
Java Code Geeks
S
Securelist
Microsoft Azure Blog
Microsoft Azure Blog
TaoSecurity Blog
TaoSecurity Blog
T
Threat Research - Cisco Blogs
M
MIT News - Artificial intelligence
A
About on SuperTechFans

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Как свёрточные нейронные сети видят мир
yvoln (Яндек · 2026-04-28 · via Все публикации подряд на Хабре

Привет, Хабр! Меня зовут Яна Вольнова, я ex-разработчик систем распознавания образов, а сейчас преподаю глубокое машинное обучение в МГТУ им. Н.Э. Баумана и пишу курсы для Яндекс Практикума, например, «Инженер по глубокому обучению нейросетей» и «Компьютерное зрение — CV». Я расскажу изнутри, как устроено нейросетевое компьютерное зрение под капотом и почему «много параметров» не всегда равно «лучшая нейросеть». Речь пойдет о классике — CNN или свёрточных нейронных сетях, а в следующей статье рассмотрим, как работают трансформеры зрения.

Статья будет отличным погружением в тему для тех, кто не работал с компьютерным зрением, либо использовал CNN чисто как инструмент, не разбираясь в механизме работы. 

Что такое компьютерное зрение

Компьютерное зрение — это любое автоматическое распознавание образов на изображениях и видео. Самые частые примеры, с которыми можно столкнуться в жизни: определение лица и эмоции в системе оплаты улыбкой, распознавание овощей при взвешивании в супермаркете или считывание номеров автомобиля при въезде на парковку. В общем, все задачи, в которых компьютер должен увидеть и как-то осмыслить то, что на изображении перед ним. 

Человеку это делать легко: для восприятия окружающего мира у него есть сложнейшая система, которая развивается много лет, с самого его рождения. Сначала мы учимся видеть мир чётко, а не размыто. Затем воспринимать его объёмным, то есть определять расстояние до предметов по тени, свету, размытию и температуре. Со временем осознаём сами предметы и их смысл. 

Всему этому нужно научить компьютер, и для такой имитации зрения (и распознавания) отлично подходят нейросети. И лучше других — свёрточные нейросети. Но есть проблема — их огромное количество, как самих архитектур, так и их модификаций, поэтому бывает сложно понять, почему одни работают, а другие нет, и как выбрать из них подходящую для своей задачи.

Так как компьютерному зрению нужно что-то видеть, для примера в статье будем использовать это фото игрушечной еды. Мы с вами легко узнаём, что тут изображено, а компьютеру придётся напрячься :-)

Designed by Freepik

Designed by Freepik

Всё, что видит компьютер — это признаки

Мы не разбиваем на этапы процесс того, как наш мозг обрабатывает фото. Мы просто видим картошку, бургер и стаканчик, не задумываясь, как это произошло. На самом деле всё, что мы видим, складывается из признаков. Признаки — это то, что отличает одну область изображения от другой, любая перемена в чём-то упорядоченном. Например, места, где меняется или цвет, или узор, или количество тени. 

На фото с нашей едой легко отделить фон — он жёлтый, ровный, неизменный. Всё, что обладает другим цветом или текстурой, сразу выделяется на нём. Например, и фон желтый, и картошка тоже жёлтая, но у неё другая текстура, поэтому мы выделяем её из фона. Но издалека мы можем сначала её не увидеть, потому что этот признак не настолько заметный.

Компьютерное зрение базируется именно на этом — нам нужно научить компьютер преобразовывать поначалу бессмысленные значения пикселей в признаки. А увидев признаки, можно начать их комбинировать и делать по ним выводы.

Признаки — это тоновые переходы, изменения света и тени, контуры. Рассмотрим их подробнее.

Изменение света и тени

Свет и тень помогают нам видеть объём. Если взять область одного цвета, то изменение этого цвета с более темного на более светлый — это признак, позволяющий понять его объём. К примеру, то, что булочка и помидор в центре более светлые, а к краю более тёмные, показывает, что они на самом деле объёмные. На фоне видно тень от бургера, она тоже выделяется изменением светлоты. Мы уже знаем, что это просто тень, но если бы не знали, воспринимали бы её как выемку в фоне, более глубокую, отдалённую от наблюдателя область. 

Каждое такое изменение света и тени — признак, который что-то говорит нам о реальной форме объекта. Плавные изменения — мягкие предметы, резкие изменения — угловатые.

Внутри каждого прямоугольника левая половина и правая половина одинаковые по цвету, но разные по свету и тени.

Тоновые переходы

Тоновый переход — это когда цвет переходит в другой, например, красный вдруг переходит в зелёный, жёлтый в черный и так далее. Такие переходы обычно показывают, где заканчивается один объект и начинается другой. Чаще всего эти переходы очень резкие (и это помогает лучше распознать признак), в отличие от светотеневых, но бывают и постепенные.

В каждом прямоугольнике половину занимает один цвет, вторую — другой, между ними лежит граница тонов, которая и является признаком.

Края, контуры

Жёсткий цветовой переход — хоть по свету, хоть по тону — это обычно контур объекта. Обратите внимание, что переход с зеленого салата на желтый фон визуально довольно слабый, но мы легко отделяем эти два предмета друг от друга именно потому, что произошло резкое изменение цвета. Даже если сами цвета похожи друг на друга.

Текстуры

Признаки бывают более сложные и комбинированные. К таким относятся текстуры и узоры — повторяющиеся последовательности простых признаков. 

Для примера держите текстуру игрушечной котлеты:

Теперь поговорим о том, как нейросети «видят» эти признаки на изображениях, чтобы дальше делать по ним выводы.

Чтобы компьютер мог увидеть признаки, используют фильтры

Признаки извлекаются из изображений с помощью фильтров — буквально тех же самых, которые используются в приложениях для редактирования фото (фильтры сглаживания, резкости, контрастности и так далее). Если применять их правильно, то каждый такой фильтр проявляет определённый признак, например, делает более заметными контуры или цветовые переходы. Изображение, на котором выделены контуры, можно рассматривать как карту местоположения признака, показывающую, где они есть на исходном фото.

С точки зрения алгоритмов фильтры — это матрицы чисел, на которые нужно умножить изображение.

Самый простой фильтр – фильтр прямых

Фильтр прямых — это вот такая матрица чисел. 

-1

0

1

-2

0

2

-1

0

1

Её называют оператором Собеля, и если поэлементно (постепенно сдвигая матрицу) умножать все числовые значения пикселей картинки на эту матрицу, суммировать и записывать результат в новое изображение, то на итоговом изображении будут выделены вертикальные линии. В итоге оно станет картой признака «вертикальная линия». Такая операция называется свёрткой, отсюда и название — свёрточные сети. Если нужны горизонтальные прямые — просто поворачиваем матрицу на 90 градусов и делаем всё то же самое.

Система свёртки с помощью фильтров довольно удобная, так как позволяет обнаружить признак в любой части изображения с помощью всего одной операции. Это значит, что нам не нужно иметь отдельный фильтр для поиска конкретного признака в левом или правом углу изображения, фильтры в процессе свёртки проходят по всему изображению — и они найдут признаки везде, в какой бы части картинки они ни были. На практике это означает, что если мы распознаем кота на фото, то абсолютно без разницы, в каком конкретно квадранте фото он находится.

Также такой фильтр позволяет увидеть переходы тона или света. Если нужны световые переходы, то выделяем один конкретный цвет и смотрим на его изменения. Сейчас покажу на примере, как это работает.

Фильтр прямых на практике

Изображения в компьютере представляются тремя смешиваемыми цветами RGB — красным, зелёным и синим. 

Получается, что каждое изображение — это тензор значений пикселей, имеющий форму ШИРИНА х ВЫСОТА х 3, где тройка — это как раз три цветовых канала: красный, зеленый, синий. Соответственно, можно выделить один цвет, например, красный — то есть взять только нулевой канал, а затем применить к нему фильтр.

В примерах я буду использовать keras и OpenCV на python. 

import cv2
from matplotlib import pyplot as plt

img = cv2.imread('test.png')[:, :, 0] # берем только нулевой канал
# буквально тот самый фильтр вертикальных линий
kernel = np.array([[-1, 0, 1],
                   [-2, 0, 2],
                   [-1, 0, 1]])
# функция, которая осуществляет процедуру свёртки, cv2.CV_64F отмечает формат, в котором отдать результат свёртки
new_img = cv2.filter2D(img, cv2.CV_64F, kernel)
plt.subplot(1, 2, 1).imshow(img, cmap='gray')
plt.axis('off')
plt.subplot(1, 2, 2).imshow(new_img, cmap='gray')
plt.axis('off')
plt.show()

Теперь прогоним нашу картинку через этот код и посмотрим на результат.

Фото слева — просто красный канал. Он тёмный там, где на фото есть красный, и светлый, где на фото красного нет. Так как жёлтый — это сочетание красного и зелёного, то он получился серым (проще говоря, красный тут тоже есть, но его меньше, чем в помидоре или на упаковке картошки).

Фото справа — вертикальные переходы, найденные в красном канале. Они отражают все места, где произошёл резкий переход в значениях красного цвета по вертикали. Тёмный цвет — резкий переход, ближе к серому — мягкий переход. Так компьютер может понять, где на фото есть признаки изменения в красном цвете. Аналогично можно повторить и с другими цветами.

Если увеличить размер фильтра с 3х3 на 11х11 (условно, сделать оператор Собеля побольше), то признаки будут обобщаться по большей площади и станут более заметными мягкие переходы.

import cv2
from matplotlib import pyplot as plt
img = cv2.imread('test.png')[:, :, 0]
new_img = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=11) # применяем оператор Собеля нужного размера
plt.subplot(1, 2, 1).imshow(img, cmap='gray')
plt.axis('off')
plt.subplot(1, 2, 2).imshow(new_img, cmap='gray')
plt.axis('off')
plt.show()

Сравните результат с предыдущими картинками:

Фильтр прямых, но по всем цветам

Если применить тот же оператор Собеля, но не к отдельному каналу изображения, а к его чёрно-белой версии, то получится карта переходов не только конкретного цвета, но и между цветами. В сравнении с анализом только красного цвета, здесь можно попробовать выделить ещё и переходы внутри бургера, тени напитка и картошки.

import cv2
from matplotlib import pyplot as plt
img = cv2.imread('test.png', cv2.IMREAD_GRAYSCALE)
new_img = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=3)
plt.subplot(1, 2, 1).imshow(img, cmap='gray')
plt.axis('off')
plt.subplot(1, 2, 2).imshow(new_img, cmap='gray')
plt.axis('off')
plt.show()

Бургер внутри стал более детальным, плюс появились явные границы теней от предметов:

Фильтр границ

Кажется, что фильтром прямых можно сразу определять и границы, но для этого лучше подходит другой инструмент. Это тоже фильтр, но он посложнее и нацелен на выделения контуров. Его называют оператором Лапласа, он помогает найти пересечения и углы. Он выглядит так:

0

1

0

1

-4

1

0

1

0

А вот его применение в коде:

import cv2
from matplotlib import pyplot as plt
img = cv2.imread('test.png', cv2.IMREAD_GRAYSCALE)
new_img = cv2.Laplacian(img, cv2.CV_64F, ksize=3)
plt.subplot(1, 2, 1).imshow(img, cmap='gray')
plt.axis('off')
plt.subplot(1, 2, 2).imshow(new_img, cmap='gray')
plt.axis('off')
plt.show()

Таким фильтром можно выделить сразу и вертикальные, и горизонтальные линии, но мягкие переходы могут потеряться. Выглядит похожим на предыдущее, но для компьютера разница огромна — вместо бессмысленных значений пикселей появились точные координаты мест, где на оригинальном фото есть границы — и вертикальные, и горизонтальные.

Для обработки цветных изображений используются более сложные фильтры, которые сами имеют три измерения. Такие фильтры сложно подобрать вручную, поэтому на помощь приходят обучаемые свёрточные нейронные сети — про них будет чуть дальше.

Поиск текстур

Текстуры сложно извлечь простым фильтром из-за их маленького размера (3х3 пикселя или 11х11 пикселей) и того факта, что они являются комбинацией более простых признаков. Поэтому здесь также приходят на помощь многоуровневые структуры — те же свёрточные нейросети, которые анализируют сначала базовые признаки, потом их сочетания, и так далее. В итоге они постепенно подбираются к настолько общим комбинациям признаков, что они способны сигнализировать о наличии на фото конкретных предметов.

Свёрточные нейросети — смотрят на мелочи и из них собирают картинку

Свёрточные сети — это способ автоматизировать процессы подбора подходящих фильтров и комбинации найденных простых признаков в более сложные. 

В первом слое свёрточной сети выделяются простейшие признаки, например, вертикальные и горизонтальные линии. Этот слой состоит из фильтров, похожих на операторы Собеля или Лапласа. 

Второй слой анализирует уже не исходное изображение, а карты местонахождения более простых признаков — тех, которые мы нашли на предыдущем этапе. Это помогает находить более сложные признаки, текстуры и полноценно работать с цветными изображениями. 

Так, постепенно проходя через слои, признаки обобщаются. К примеру, на первом слое признак — это просто какая-то чёрточка, на втором — уже линия, на третьем — кривая, на четвертом — круг, на пятом — восьмёрка, и так далее. 

Так как каждый следующий слой обобщает результаты предыдущих, на каждом следующем слое фильтр «видит» всё большую область картинки. Получается, что сначала выделяются простые (они же — локальные) признаки. На следующем этапе фильтры применяются к области того же размера, но не на исходном изображении, а  уже на картах признаков предыдущего слоя. На них сами признаки уже содержат информацию не о конкретных пикселях, а о небольших областях изображения. И так, постепенно собирая признаки всё с большей площади, приходят ко всему изображению и глобальному контексту. Сеть будто бы рассматривает изображение вблизи, а потом постепенно отдаляется, чтобы увидеть всю картину.

Теперь снова расчехляем Python и пишем код, чтобы понаблюдать за процессом изнутри.

Первый слой — собираем простейшие признаки

Мы не будем писать свою нейросеть с нуля, а используем для этого классику жанра — простую предобученную свёрточную сеть VGG16, и посмотрим, какие фильтры содержатся в её первом слое после обучения.

import pandas as pd
from tensorflow.keras.applications.vgg16 import VGG16
from tensorflow.keras.applications.vgg16 import preprocess_input
from tensorflow.keras.preprocessing.image import load_img
from tensorflow.keras.preprocessing.image import img_to_array
from tensorflow.keras.models import Model
from matplotlib import pyplot
from numpy import expand_dims
from matplotlib import pyplot as plt

model = VGG16()

# Выводим номера ее свёрточных слоев
for layer in model.layers:
    if '_conv' in layer.name:
        filters, bias = layer.get_weights()
        print(layer.name, filters.shape)

# В этой модели первый слой - входной, второй - как раз свёрточный
filters, bias = model.layers[1].get_weights()


# Отобразим половину фильтров первого свёрточного слоя  - 32 из 64
n_filters = 32

counter = 1
fig = plt.figure(figsize=(12,1))
for i in range(n_filters):
    f = filters[:,:,:,i]
    for j in range(3):
        plt.subplot(3, n_filters, counter)
        plt.imshow(f[:,:,j], cmap='gray')
        plt.axis('off')
        counter += 1

plt.show()

Фильтры, похожие на операторы Собеля и Лапласа (и многие другие), для начала извлекают простые признаки вроде цветовых переходов. Вот примеры фильтров для поиска этих признаков:

Чтобы понять, как выглядит изображение после прохода через первый слой, отрежем от него остальные слои и посмотрим на результат.

import cv2
# Сокращаем модель, оставив только один свёрточный слой
model_truncated = Model(inputs=model.inputs , outputs=model.layers[1].output)

img = cv2.imread('test.png')
img = expand_dims(img, axis=0)

img = preprocess_input(img)

features = model_truncated.predict(img)[0]

fig = plt.figure(figsize=(15,15))
for i in range(features.shape[2]):

    plt.subplot(8, 8, i+1)
    plt.imshow(features[:,:,i], cmap='gray')
    plt.axis('off')
    
plt.show()

Так выглядят карты местонахождения признаков, выделенных первыми 16-ю фильтрами в первом свёрточном слое. Некоторые из них похожи на результат работы оператора Собеля или Лапласа, другие отличаются. Но все они выделили разные базовые признаки — местоположения основных цветов, переходы между ними и контуры:

Второй слой— ищем группы более сложных признаков

Теперь точно так же посмотрим, как выглядят карты признаков на следующих свёрточных слоях — 4, 9 и последнем (слой номер 17). Добавим этот код к предыдущему:

blocks = [4, 9, 17]
outputs = [model.layers[i].output for i in blocks]

model_conv = Model(inputs=model.inputs, outputs=outputs)

feature_map = model_conv.predict(img)

for i, fmap in zip(blocks, feature_map):
    fig = plt.figure(figsize=(15,15))
    fig.suptitle("свёрточный блок {}".format(i))
    for i in range(features.shape[2]):
        plt.subplot(8,8,i+1)
        plt.imshow(fmap[0,:,:,i], cmap='gray')
        plt.axis('off')
    
plt.show()

Смотрите, на четвёртом слое признаки начинают обобщаться: важные границы становятся более заметными, меньше внимания уделяется областям с одинаковым цветом, больше — их переходам.

На девятом слое признаки обобщаются ещё сильнее, изображение словно становится размытым. На самом деле теперь каждая белая точка — это какой-то обобщённый признак (мы не знаем, какой, но компьютер уже знает). Например, что в этом углу был зафиксирован красный предмет, а в другом — зеленый.

Семнадцатый слой — переходим к поиску объектов

На последнем слое мелкие признаки уже не имеют смысла. Это слой, после которого начинается процесс принятия решения о том, какие образы есть на фото. Здесь карты становятся совсем простыми. Каждая белая точка означает, что был найден или не найден какой-то крупный важный признак, например, булочка, картофель или стакан. На этом этапе логику свёрточной сети почти невозможно расшифровать, равно как и понять, какая карта отвечает за какой признак.

Ради интереса посмотрите на карты признаков, которые нашла нейросеть на этом слое, и сравните их местоположение с оригинальной картинкой — и попробуйте понять, почему нейросеть пометила именно эти области:

Примеры из реальных архитектур

Используемая до этого VGG16 — это одна из первых свёрточных архитектур. Её точность довольно ограничена (бенчмарк ImageNet — 71,3%), при том, что параметров в ней сравнительно много — 138,4 млн. Однако, как вы могли увидеть по семнадцатому слою, в ней нет смысла добавлять ещё больше слоев  — на 17 слое уже очень обобщённые признаки. Поэтому новые архитектуры отличаются не только количеством слоёв, но и их механизмами, что позволяет более качественно обучать сети.

Архитектура посложнее и поновее

Такой архитектурой можно назвать ResNet50. Подобные ей модели много лет были стандартом, уступив это место совсем недавно, когда появились трансформеры зрения. Её точность на бенчмарке — 74,9%, количество параметров при этом всего 25,6 млн.

Аналогично посмотрим ее фильтры и карты признаков.

Фильтры первого свёрточного слоя выглядят так:

Они более затейливые, чем у VGG16, а значит могут выделять более сложные признаки, такие как кривые, пересечения трех линий и прочие. Это достигнуто за счет увеличения размера фильтра для первого слоя. В VGG16 были 3х3, а здесь 7х7.

Изображения на выходе первого слоя получаются такие:

Они очень похожи на результаты VGG16, но выглядят более сглаженными, будто перед нами уже четвёртый слой VGG16. Также есть отдельные карты, на которых отмечены только какие-то специфические признаки, например, текстура котлеты.

Посмотрим также на промежуточный 19 слой, 103 (что-то посерединке) и 171 (последний свёрточный). На самом деле свёрточных слоев в этой модели меньше чем 171, но они имеют такие индексы, потому что перемежаются другими слоями.

Итак, 19 слой — признаки заметно обобщаются, снова исчезает информация о моноцветовых областях, внимание уделяется границам.

103 слой — лютая абстракция. Кое-где угадываются местоположения трех неких объектов, но не более.

171 слой — окончательное абстрагирование. Карты отмечают только наличие признаков объектов на изображении. Супрематисты в восторге.

Вообще сложная архитектура жесть

Для примера рассмотрим ещё более современную архитектуру — ConvNext, она появилась как ответ трансформерам зрения. Её точность на бенчмарке — 81,3% при всего лишь 28,6 млн параметров. Получается, что у неё размер как у ResNet50, но точность на 6% выше.

Ее фильтры первого слоя выглядят так:

Они тоже 7х7, но при этом совершенно другие. Это связано с тем, что их стало больше. Раз фильтров больше, значит, каждый из них может стать более узкоспециализированным. Для разных, даже самых мелких переходов теперь есть свой фильтр. Это отражается и на картах признаков первого слоя:

За счёт большого количества разнообразных фильтров признаки тоже очень разнообразны. Изображения уже выглядят так, будто прошли много стадий абстрагирования признаков. По аналогии с ResNet посмотрим на слой из начала — 7, из середины — 86, и последний — 129.

7 — даже в самом начале работы нейросети становится сложнее понять, что значит тот или иной обобщённый признак:

86 — абстракция, где даже не угадывается местоположение исходных трех предметов.

129 – только обобщенные абстрактные признаки.

А зачем нам последний слой, если на нём сплошная абстракция?

Дело в том, что компьютер смотрит на картинки не так, как человек. Для нас это абстракция, для нейронной сети — координаты признаков. Мы пытаемся рассмотреть в этом какой-то рисунок, но на самом деле это просто карта местоположения объектов. На ранних слоях нейронная сеть обозначает местонахождение палочек, чёрточек, цветовых пятен — простейших признаков. Для нас это выглядит как рисунки контуров предметов. 

На поздних слоях нейронные сети отмечают местоположение более сложных объектов — где находится бургер, где картошка, где тень от каждого из них. Белые пятна — наличие какого-то признака, чёрный фон — отсутствие. Поэтому то, что для нас — случайные белые пятна, для компьютера карта расположения объектов или их отдельных признаков на изображении. И чем глубже слой, тем более абстрактным становится объект. На 129 слое одно белое пятно может обозначать многоквартирный дом или лицо конкретного человека.

Задача последних слоёв — постараться вытащить более крупные, общие признаки, которые не могли увидеть предыдущие слои. Если предыдущих слоёв достаточно (например, контрастная картинка с чёткими границами и простая для распознавания) — можно так глубоко и не копать. 

Но компьютер не знает, глубоко это или нет, — он просто делает свою работу и старается не пропустить ни одной детали. Бывает так, что последние слои извлекают какую-то дополнительную информацию, которая может повлиять на итоговый результат распознавания. А может и не повлиять — и про это как раз расскажу ниже.

Заключение — как выбрать, сколько слоев нужно

Сегодня мы посмотрели, как нейросети представляют признаки на разных слоях. Соотношение точности, количества параметров и визуального представления карт признаков показывает, что лучшая точность не напрямую связана с размером нейронной сети или её глубиной. Современные архитектуры позволяют получить лучшую точность за тот же или меньший набор параметров.

  • Если задача простая, если признак очевидный — нескольких слоёв будет достаточно. 

  • Для создания универсальных моделей нужна большая глубина, чтобы отразить большее возможное разнообразие комбинаций признаков. 

  • Чем больше фильтров в слое, тем больше признаков он способен извлечь. 

  • Чем больше размер фильтра, тем более сложные признаки он способен извлечь, но это вычислительно дороже, так как нужно больше операций поэлементного умножения.

  • Чем больше слоёв — тем больше возможностей у компьютера обобщить сложные и абстрактные признаки. Всегда ли это нужно — отдельный вопрос.

А в следующий раз мы рассмотрим трансформеры зрения и чем они лучше (или хуже) классических свёрточных сетей.