Введение в анализ данных¶
Домашнее задание 7. Кластеризация и понижение размерности¶
Правила, прочитайте внимательно:
- Выполненную работу нужно отправить телеграм-боту
@thetahat_ds25_bot
. Для начала работы с ботом каждый раз отправляйте/start
. Дождитесь подтверждения от бота, что он принял файл. Если подтверждения нет, то что-то не так. Работы, присланные иным способом, не принимаются. - Дедлайн см. в боте. После дедлайна работы не принимаются кроме случаев наличия уважительной причины.
- Прислать нужно ноутбук в формате
ipynb
. Если вы строите интерактивные графики, их стоит прислать в формате html. - Следите за размером файлов. Бот не может принимать файлы весом более 20 Мб. Если файл получается больше, заранее разделите его на несколько.
- Выполнять задание необходимо полностью самостоятельно. При обнаружении списывания всем участникам списывания дается штраф -2 балла к итоговой оценке за семестр.
- Решения, размещенные на каких-либо интернет-ресурсах, не принимаются. Кроме того, публикация решения в открытом доступе может быть приравнена к предоставлении возможности списать.
- Обратите внимание на правила использования ИИ-инструментов при решении домашнего задания.
- Код из рассказанных на занятиях ноутбуков можно использовать без ограничений.
- Для выполнения задания используйте этот ноутбук в качестве основы, ничего не удаляя из него. Можно добавлять необходимое количество ячеек.
- Комментарии к решению пишите в markdown-ячейках.
- Выполнение задания (ход решения, выводы и пр.) должно быть осуществлено на русском языке.
- Решение проверяется системой ИИ-проверки
ThetaGrader. Результат проверки валидируется и исправляется человеком, после чего комментарии отправляются студентам.
- Если код будет не понятен проверяющему, оценка может быть снижена.
- Никакой код из данного задания при проверке запускаться не будет. Если код студента не выполнен, недописан и т.д., то он не оценивается.
- Код из рассказанных на занятиях ноутбуков можно использовать без ограничений.
Правила оформления теоретических задач:
- Решения необходимо оформить в виде $\LaTeX$ в markdown-ячейках. Иные способы (в т.ч. фотографии) не принимаются.
- Если вы не знаете $\LaTeX$, используйте ИИ-инструменты для оформления черновика решения. Примеры были показаны на лекции 2 по ИИ-инструментам.
- В решениях поясняйте, чем вы пользуетесь, хотя бы кратко.
- Решение, в котором есть только ответ, и отсутствуют вычисления, оценивается в 0 баллов.
Важно!!! Правила заполнения ноутбука:
- Запрещается удалять имеющиеся в ноутбуке ячейки, менять местами положения задач.
- Сохраняйте естественный линейный порядок повествования в ноутбуке сверху-вниз.
- Отвечайте на вопросы, а также добавляйте новые ячейки в предложенных местах, которые обозначены
...
. - В markdown-ячейка, содержащих описание задачи, находятся специальные отметки, которые запрещается модифицировать.
- При нарушении данных правил работа может получить 0 баллов.
Баллы за задание:
- Задача 1 — 30 баллов
- Задача 2 — 30 баллов
- Задача 3 — 120 баллов
Баллы учитываются в факультативной части курса и не влияют на оценку по основной части.
# Bot check
# HW_ID: fpmi_ad7
# Бот проверит этот ID и предупредит, если случайно сдать что-то не то.
# Status: not final
# Перед отправкой в финальном решении удали "not" в строчке выше.
# Так бот проверит, что ты отправляешь финальную версию, а не промежуточную.
# Никакие значения в этой ячейке не влияют на факт сдачи работы.
При необходимости установите библиотеку-расширение для plotly
, позволяющую рисовать картинки на всплывающих окнах.
pip install dash
import io
import os
import base64
from typing import Dict, List, Optional, Tuple, Union
import numpy as np
import pandas as pd
from tqdm.cli import tqdm
import matplotlib.pyplot as plt
import seaborn as sns
from PIL import Image
import plotly.express as px
from dash import Dash, dcc, html, Input, Output, no_update, callback
from sklearn.cluster import KMeans
from sklearn.decomposition import PCA
import torch
from transformers import ViTImageProcessor, ViTModel
sns.set_theme(style="darkgrid", palette="Set2")
Внимание! Файл с решением может быть тяжелым. Если он весит 20 Мб и более, заранее разделите его на несколько частей.
Задача 1.¶
Докажите, что метод KMeans для евклидовой метрики делит все пространство объектов на выпуклые многоугольники, возможно, неограниченные.
Можно использовать теоремы и свойства, которые у вас были ранее на обязательных курсах (но не курсах по выбору). При этом нужно привести четкое обоснование, как вы пользуетесь соответствующими свойствами, привести все обозначения.
Задача 2.¶
Как мы знаем из лекции, в пространствах большой размерности расстояния между случайными объектами становятся неинформативными. Эта проблема известна как проклятие размерности, и она влечет соответствующие ограничения на использование методов, основанных на использовании расстоянии между объектами.
В этой задаче предлагается промоделировать ситуацию понижения размерности. Сгенерируйте достаточно большое количество точек в единичном кубе в пространстве некоторой размерности. Пример кода дан ниже
sample_size = 1000
dimention = 100
sample = np.random.uniform(size=(sample_size, dimention))
Повторите генерацию для нескольких размерностей пространства от 2 до 1000. Используйте не менее 7 различных значений размерностей пространства.
...
Для каждой размерности посчитайте норму каждой точки, тем самым получая набор значений расстояния от 0 до случайной точки. Нормируйте все расстояния, поделив на среднее полученных расстояний для каждой размерности пространства.
...
Нарисуйте на одном графике гистограммы или KDE-оценки плотности нормированных расстояний для каждой размерности пространства. Не забывайте поддерживать информативность графиков.
...
Сделайте выводы
...
Задача 3.¶
Возможно, эту задачу будет оптимальнее сделать в отдельном файле или после перезапуска ядра. Если не хватает оперативки, попробуйте использовать более простые модели. Если все равно не помогает — спросите в чате.
В этой задаче мы попробуем кластеризовать различными способами изображения котиков из датасета с семинара.
Скачайте данные, загрузите их и отрисуйте несколько примеров. Если вы используете Colab, оптимальнее всего будет закинуть zip-архив cats.zip
в файловое пространство (кнопка папки на левой панели) и распаковать его с помощью !unzip cats.zip
.
# Загружаем картинки
# ...
cats = ...
# Визуализируем примеры
# ...
1. Свойства метрики в пространстве котиков¶
Прежде всего давайте исследуем, наблюдается ли проблема проклятия размерности в пространстве котиков, фактически повторив исследование предыдущей задачи. Для этого выберите не менее 10000 случайных пар изображений и посчитайте расстояния между ними. Визуализируйте гистограмму или KDE-оценку плотности нормированных расстояний.
Повторите те же действия к результату применения PCA, рассмотрев разное количество компонент, например, для 30, 100 и 500. Обратите внимание, что PCA нужно обучать на всех изображениях, а не только для тех, для которых вы будете считать расстояния. Примеры обучения PCA можно посмотреть в ноутбуке с занятия.
...
Сделайте выводы
2. Кластеризация котиков по вектору изображения¶
Начнем эксперименты с самого простого способа: каждое изображение можно вытянуть в вектор размерности $64 \cdot 64 \cdot 3$, их и попытаемся кластеризовать. Обучите по этим объектам метод KMeans из sklearn, пример можно посмотреть в ноутбуке по кластеризации
%%time
...
Теперь отобразим изображения на плоскость с помощью PCA и визуализируем полученные предсказания цветом, как это было сделано на семинаре для ответов на вопросы в бот. Ниже уже реализована функция отрисовки visualize_images_clusterisation
, вам требуется лишь применить PCA и вызвать функцию с правильными параметрами.
В процессе работы над заданием для детального исследования кластеров стоит передавать
use_dash=True
, в этом случае всё будет рисоваться со всплывающими картинками. А в конце, перед сдачей работы, необходимо перерисовать графики сuse_dash=False
, тогда они нарисуются с помощью простого plotly и сохранятся в ноутбукеЕсли вы работаете в Colab, и с опцией
use_dash=False
у вас не отрисовывается график, попробуйте посмотреть решение проблемы тут.Если вы работаете в Jupyter Notebook, и у вас не отображаются графики, попробуйте нажать кнопку
Not Trusted
на верхней панели справа.Если совсем никак не получается, сохраните графики в html и отправьте боту.
def visualize_images_clusterisation(
images: np.ndarray,
projection: np.ndarray,
clusters: np.ndarray,
port: Optional[int] = None,
use_dash: bool = False,
) -> Optional[Dash]:
"""
Визуализирует кластеризацию изображений с возможностью просмотра изображений при наведении.
Создает интерактивный scatter plot проекции изображений с двумя режимами:
1. Режим Jupyter (use_dash=False): показывает статичный plotly график
2. Режим Dash (use_dash=True): запускает интерактивный сервер с hover-эффектом
Параметры:
images: Массив изображений формы (n_samples, height, width, channels)
projection: 2D проекция эмбеддингов формы (n_samples, 2)
clusters: Массив меток кластеров формы (n_samples,)
port: Порт для запуска Dash-приложения (None - случайный порт)
use_dash: Флаг использования интерактивного Dash-режима
Возвращает:
При use_dash=True возвращает Dash приложение, иначе None
"""
plotly.offline.init_notebook_mode()
# Рисуем график с точками как на семинаре
fig = px.scatter(
x=projection[:, 0],
y=projection[:, 1],
hover_name=clusters,
hover_data={"image_idx": list(range(len(images)))},
color=clusters.astype(str),
width=1000,
height=800,
title="PCA проекция изображений на плоскость",
size=[1] * len(images),
size_max=12,
)
if not use_dash:
fig.show()
return
# Добавляем во всплывающее окошко (hover box) изображения
fig.update_traces(
hoverinfo="none",
hovertemplate=None,
)
app = Dash(__name__)
app.layout = html.Div(
className="container",
children=[
dcc.Graph(id="graph-2-dcc", figure=fig, clear_on_unhover=True),
dcc.Tooltip(id="graph-tooltip-2", direction="bottom"),
],
)
@callback(
Output("graph-tooltip-2", "show"),
Output("graph-tooltip-2", "bbox"),
Output("graph-tooltip-2", "children"),
Output("graph-tooltip-2", "direction"),
Input("graph-2-dcc", "hoverData"),
)
def display_hover(
hoverData: Optional[Dict],
) -> Tuple[bool, Union[Dict, str], List[html.Img], str]:
"""
Callback функция для обработки hover-событий и отображения изображений во всплывающем окне.
Параметры:
hoverData: Данные о точке, на которую наведен курсор. Содержит:
- coordinates (координаты точки)
- pointIndex (индекс точки)
- bbox (границы точки)
- customdata (дополнительные данные)
Возвращает кортеж:
1. Флаг показа/скрытия tooltip (bool)
2. Bounding box точки (dict) или no_update
3. Список HTML-элементов для отображения (List[html.Img])
4. Направление отображения tooltip (str)
"""
if hoverData is None:
return False, no_update, no_update, no_update
# Достаем индекс картинки, который выше положили в hover_data
hover_data = hoverData["points"][0]
image_idx = hover_data["customdata"][0]
# И получаем само изображение кота
image = Image.fromarray(images[image_idx].reshape(64, 64, 3))
# Преобразовываем изображение в base64 кодировку
buffer = io.BytesIO()
image.save(buffer, format="jpeg")
encoded_image = base64.b64encode(buffer.getvalue()).decode()
image_url = "data:image/jpeg;base64, " + encoded_image
image_children = [
html.Img(
src=image_url,
style={"width": "196px"},
),
]
return True, hover_data["bbox"], image_children, "top"
if port is None:
port = str(np.random.randint(5000, 15000))
app.run(port=port, debug=True, jupyter_height=800)
return app
...
Для каждого кластера нарисуйте по 5-10 типичных изображений, то есть ближайших к центру кластера. Похожую операцию мы видели на занятии в ноутбуке по кластеризации.
Примечание. Для одного кластера рисуйте картинки "в строчку". Так будет удобно как вам самим, так и проверяющему. Примеры можно посмотреть в ноутбуке по PCA.
...
Наблюдаются ли какие-то закономерности в изображениях внутри одного кластера? Если да, то опишите отличительные черты кластеров.
...
Подумайте, чем может быть плох такой подход? Какая проблема могла возникнуть и возникла ли? Обратите внимание на проведенное ранее исследование.
...
3. PCA + кластеризация¶
Попробуем уменьшить размерность перед кластеризацией с помощью PCA, спроектировав изображения на первые несколько главных компонент
Примечание. Не стоит брать больше 100 главных компонент
Сделайте кластеризацию:
%%time
...
Визуализируйте полученные кластера:
...
Нарисуйте по 5-10 типичных изображений для новых кластеров
...
Опишите отличительные черты кластеров:
...
Сильно ли они отличаются от предыдущего способа?
...
4. Нейросетевые эмбеддинги + кластеризация¶
Попробуем применить к изображениям такой же подход, как с текстами. То есть возьмем хорошую уже обученную нейросеть, получим с ее помощью эмбеддинги изображений и будем делать кластеризацию этих эмбеддингов.
Выберите на huggingface модель для получения эмбеддингов изображений.
Примечания
- Нам нужны
CV
модели для задачи извлечения признаков (в фильтрах huggingface называютсяImage Feature Extraction
).- Не выбирайте слишком тяжелые модели (с суффиксами
-huge
,-giant
,-large
и прочим), нам это ни к чему, так как картинки маленькие и простые. К тому же это замедлит предсказание эмбеддингов.- Обычно в карточке модели есть пример применения, который можно скопировать и как в семинаре в цикле применить ко всем батчам изображениям.
- Некоторые модели могут возвращать не эмбеддинг размерности
(D,)
, а матрицу признаков размерности(n, D)
, в этому случае надо применить average-pooling, усреднив по предпоследней оси.
Загрузите модель, примените к одному тестовому изображению кота и посмотрите на размерность полученного тензора
example_image = cats[0].reshape(64, 64, 3)
# Загрузка и тестовое применение модели
...
Теперь примените загруженную модель ко всем картинкам
...
Сделайте кластеризацию полученных эмбеддингов
...
Визуализируйте полученные кластера:
...
Нарисуйте по 5-10 типичных изображений для новых кластеров
...
Опишите отличительные черты кластеров:
...
Чем отличаются кластеры, полученные этим способом от первых двух?
...