Физтех.Статистика
Скачать ipynb
Введение в анализ данных¶
Домашнее задание 7. Кластеризация и понижение размерности¶
Правила, прочитайте внимательно:
- Выполненную работу нужно отправить телеграм-боту
@miptstats_ds24_bot
. Для начала работы с ботом каждый раз отправляйте/start
. Работы, присланные иным способом, не принимаются. - Дедлайн см. в боте. После дедлайна работы не принимаются кроме случаев наличия уважительной причины.
- Прислать нужно ноутбук в формате
ipynb
. - Следите за размером файлов. Бот не может принимать файлы весом более 20 Мб. Если файл получается больше, заранее разделите его на несколько.
- Выполнять задание необходимо полностью самостоятельно. При обнаружении списывания все участники списывания будут сдавать устный зачет.
- Решения, размещенные на каких-либо интернет-ресурсах, не принимаются. Кроме того, публикация решения в открытом доступе может быть приравнена к предоставлении возможности списать.
- Для выполнения задания используйте этот ноутбук в качестве основы, ничего не удаляя из него. Можно добавлять необходимое количество ячеек.
- Комментарии к решению пишите в markdown-ячейках.
- Выполнение задания (ход решения, выводы и пр.) должно быть осуществлено на русском языке.
- Если код будет не понятен проверяющему, оценка может быть снижена.
- Никакой код из данного задания при проверке запускаться не будет. Если код студента не выполнен, недописан и т.д., то он не оценивается.
Правила оформления теоретических задач:
- Решения необходимо прислать одним из следующих способов:
- фотографиями в правильной ориентации, где все четко видно, а почерк разборчив,
- отправив их как файл (
pdf
/png
/jpg
) боту вместе с ноутбуком - или вставив ее в ноутбук посредством
Edit -> Insert Image
при редактировании markdown-ячейки (фото, вставленные ссылкой, не принимаются);
- отправив их как файл (
- в виде $\LaTeX$ в markdown-ячейках.
- фотографиями в правильной ориентации, где все четко видно, а почерк разборчив,
- Решения не проверяются, если какое-то требование не выполнено. Особенно внимательно все проверьте в случае выбора второго пункта (вставки фото в ноутбук). Неправильно вставленные фотографии могут не передаться при отправке. Для проверки попробуйте переместить
ipynb
в другую папку и открыть его там. - В решениях поясняйте, чем вы пользуетесь, хотя бы кратко. Например, если пользуетесь независимостью, то достаточно подписи вида "X и Y незав."
- Решение, в котором есть только ответ, и отсутствуют вычисления, оценивается в 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
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.¶
В этой задаче мы попробуем кластеризовать различными способами изображения котиков из датасета с семинара.
Скачайте данные, загрузите их и отрисуйте несколько примеров.
# Загружаем картинки
# <...>
cats = <...>
# Визуализируем примеры
# <...>
1. Свойства метрики в пространстве котиков¶
Прежде всего давайте исследуем, наблюдается ли проблема проклятия размерности в пространстве котиков, фактически повторив исследование предыдущей задачи. Для этого выберите не менее 10000 случайных пар изображений и посчитайте расстояния между ними. Визуализируйте KDE-оценку плотности нормированных расстояний.
Повторите те же действия к результату применения PCA, рассмотрев разное количество компонент, например, для 30, 100 и 500. Обратите внимание, что PCA нужно обучать на всех изображениях, а не только для тех, для которых вы будете считать расстояния. Примеры обучения PCA можно посмотреть в ноутбуке с занятия.
Сделайте выводы
%%time
# <...>
Теперь отобразим изображения на плоскость с помощью PCA и визуализируем полученные предсказания цветом, как это было сделано на семинаре для ответов на вопросы в бот. Ниже уже реализована функция отрисовки visualize_images_clusterisation
, вам требуется лишь применить PCA и вызвать функцию с правильными параметрами.
def visualize_images_clusterisation(images, projection, clusters, port=None):
"""
Визуализиует двумерную проекцию эмбеддингов изображений,
во всплывающем окне показывает сами изображения
:param images: набор изображений
:param projection: двумерная проекция изображений или эмбеддингов изображений
:param clusters: предсказанные кластера
:param port: опциональный параметр, порт на котором поднимается визуализация:
одинаковые порты - одинаковые графики, но если в ноутбуке открыто много портов, то могут начаться лаги
"""
# Рисуем график с точками как на семинаре
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,
)
# Добавляем во всплывающее окошко (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):
"""
Настраивает всплывающее окно hover box.
:param hoverData: данные соответствующей точки
"""
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 типичных изображений для новых кластеров
# <...>
Опишите отличительные черты кластеров:
<...>
Чем отличаются кластеры, полученные этим способом от первых двух?
<...>