<jupyter_start><jupyter_text>[Уменьшение количества цветов изображения](https://www.coursera.org/learn/vvedenie-mashinnoe-obuchenie/programming/yWVid/umien-shieniie-kolichiestva-tsvietov-izobrazhieniia)  ВведениеСамый распространенный тип задач машинного обучения — это задачи обучения с учителем. В них имеется обучающая выборка, для каждого объекта которой есть ответ, и нужно научиться предсказывать эти ответы для новых объектов. В такой постановке можно строго определить критерии качества.Если же имеются лишь объекты, а ответов для них нет, то все равно можно пытаться найти в данных некую структуру. Задачи, которые ищут закономерности в неразмеченных выборках, называют задачами обучения без учителя. Типичный пример такой задачи — кластеризация, где требуется найти группы похожих объектов.Кластеризация может использоваться для самых разных целей. В этом задании мы попробуем группировать схожие пиксели на изображении. Такой подход позволяет переходить к суперпиксельному представлению изображений, которое является более компактным и лучше подходит для решения ряда задач компьютерного зрения.<jupyter_code>import pandas as pd
import numpy as np
from skimage.io import imread
import skimage
import pylab
from itertools import chain
from sklearn.cluster import KMeans
import math

%matplotlib inline<jupyter_output><empty_output><jupyter_text>1. Загрузите картинку parrots.jpg. Преобразуйте изображение, приведя все значения в интервал от 0 до 1. Для этого можно воспользоваться функцией img_as_float из модуля skimage. Обратите внимание на этот шаг, так как при работе с исходным изображением вы получите некорректный результат.<jupyter_code>image = imread('./data/parrots.jpg')
scale_image = skimage.img_as_float64(image)

width, height = len(scale_image[1]), len(scale_image)<jupyter_output><empty_output><jupyter_text>2. Создайте матрицу объекты-признаки: характеризуйте каждый пиксель тремя координатами - значениями интенсивности в пространстве RGB.<jupyter_code>listmerge = lambda lst: list(chain(*chain(*lst)))
val = listmerge(scale_image)
data = pd.DataFrame({'R': val[::3], 'G': val[1::3], 'B': val[2::3]})
data.head()<jupyter_output><empty_output><jupyter_text>3. Запустите алгоритм K-Means с параметрами init='k-means++' и random_state=241. После выделения кластеров все пиксели, отнесенные в один кластер, попробуйте заполнить двумя способами: медианным и средним цветом по кластеру.<jupyter_code># kmeans = KMeans(init='k-means++', random_state=241, n_jobs=-1)
kmeans = KMeans(init='k-means++', random_state=241)
kmeans
kmeans8 = kmeans.fit(data)
cluster = pd.Series(kmeans8.labels_, name='lable')
data_c = data.join(cluster)
data_c.head()
data_mean = data_c.copy()
for i in range(8):
    data_mean.loc[data_mean['lable'] == i, 'R'] = data_c[data_c['lable'] == i]['R'].mean()
    data_mean.loc[data_mean['lable'] == i, 'G'] = data_c[data_c['lable'] == i]['G'].mean()
    data_mean.loc[data_mean['lable'] == i, 'B'] = data_c[data_c['lable'] == i]['B'].mean()

data_mean.head()
data_median = data_c.copy()
for i in range(8):
    data_median.loc[data_median['lable'] == i, 'R'] = data_c[data_c['lable'] == i]['R'].median()
    data_median.loc[data_median['lable'] == i, 'G'] = data_c[data_c['lable'] == i]['G'].median()
    data_median.loc[data_median['lable'] == i, 'B'] = data_c[data_c['lable'] == i]['B'].median()

data_median.head()
# _ = np.median(data_c.loc[data_c['lable'] == i, ['R', 'G', 'B']].to_numpy())
# data_median.loc[data_median['lable'] == 3, ['R', 'G', 'B']] = _
# # data_median.loc[data_median['lable'] == 3, 'R'] = _
# # data_median.loc[data_median['lable'] == 3, 'G'] = _
# # data_median.loc[data_median['lable'] == 3, 'B'] = _
# data_median.loc[data_median['lable'] == 3]<jupyter_output><empty_output><jupyter_text>4. Измерьте качество получившейся сегментации с помощью метрики PSNR. Эту метрику нужно реализовать самостоятельно ([см. определение](https://ru.wikipedia.org/wiki/%D0%9F%D0%B8%D0%BA%D0%BE%D0%B2%D0%BE%D0%B5_%D0%BE%D1%82%D0%BD%D0%BE%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D1%81%D0%B8%D0%B3%D0%BD%D0%B0%D0%BB%D0%B0_%D0%BA_%D1%88%D1%83%D0%BC%D1%83)).<jupyter_code># MSE = lambda I, K, width, height: 1 / (3 * width * height) * sum([(i['R'] - j['R'])**2 + (i['G'] - j['G'])**2 + (i['B'] - j['B'])**2 for (_, i), (_, j)  in zip(I.iterrows(), K.iterrows())])
# mse = MSE(data_c, data_mean, width, height)
# PSNR = lambda MAX, MSE: 20 * math.log(MAX / math.sqrt(MSE))
# psnr_ = PSNR(1, mse)
# psnr_
# MSE = lambda I, K, width, height: 1 / (3 * width * height) * sum([(i['R'] - j['R'])**2 + (i['G'] - j['G'])**2 + (i['B'] - j['B'])**2 for (_, i), (_, j)  in zip(I.iterrows(), K.iterrows())])
# MSE = lambda I, K, width, height: 1 / (3 * width * height) * sum([(i['R'] - j['R'] + i['G'] - j['G'] + i['B'] - j['B'])**2 for (_, i), (_, j)  in zip(I.iterrows(), K.iterrows())])
# PSNR = lambda I, K, width, height, MAX: 20 * math.log(MAX / math.sqrt(MSE(I, K, width, height)))
# 42.452351239513895
# (data_c['R'] - data_mean['R']).head()
# ((data_c['R'] - data_mean['R'])**2).sum()
MSE = lambda I, K, width, height: ((I['R'] - K['R'])**2 + (I['G'] - K['G'])**2 + (I['B'] - K['B'])**2).sum() / (3 * width * height) # правильная
# MSE = lambda I, K, width, height: ((I['R'] - K['R'])**2 / (width * height) + (I['G'] - K['G'])**2 / (width * height)  + (I['B'] - K['B'])**2 / (width * height)).sum() / 3
PSNR = lambda I, K, width, height, MAX: 20 * math.log10(MAX / math.sqrt(MSE(I, K, width, height)))
psnr_mean = PSNR(data_c, data_mean, width, height, 1)
psnr_mean
psnr_median = PSNR(data_c, data_median, width, height, 1)
psnr_median<jupyter_output><empty_output><jupyter_text>5. Найдите минимальное количество кластеров, при котором значение PSNR выше 20 (можно рассмотреть не более 20 кластеров, но не забудьте рассмотреть оба способа заполнения пикселей одного кластера). Это число и будет ответом в данной задаче.<jupyter_code># means = []
# medians = []
# clustes = []
# for count_cluster in range(1, 20):
#     kmeans = KMeans(n_clusters=count_cluster, init='k-means++', random_state=241, n_jobs=-1).fit(data)
#     cluster = pd.Series(kmeans.labels_, name='lable')
#     data_c = data.join(cluster)
    
#     data_mean = data_c.copy()
#     for i in range(count_cluster):
#         data_mean.loc[data_mean['lable'] == i, 'R'] = data_c[data_c['lable'] == i]['R'].mean()
#         data_mean.loc[data_mean['lable'] == i, 'G'] = data_c[data_c['lable'] == i]['G'].mean()
#         data_mean.loc[data_mean['lable'] == i, 'B'] = data_c[data_c['lable'] == i]['B'].mean()
    
#     psnr_mean = PSNR(data_c, data_mean, width, height, 1)

#     data_median = data_c.copy()
#     for i in range(count_cluster):
#         data_median.loc[data_median['lable'] == i, 'R'] = data_c[data_c['lable'] == i]['R'].median()
#         data_median.loc[data_median['lable'] == i, 'G'] = data_c[data_c['lable'] == i]['G'].median()
#         data_median.loc[data_median['lable'] == i, 'B'] = data_c[data_c['lable'] == i]['B'].median()
    
#     psnr_median = PSNR(data_c, data_median, width, height, 1)
    
#     clustes.append(count_cluster)
#     means.append(psnr_mean)
#     medians.append(psnr_median)
def compute_psnr(count_cluster):

    kmeans = KMeans(n_clusters=count_cluster, init='k-means++', random_state=241).fit(data)
    cluster = pd.Series(kmeans.labels_, name='lable')
    data_c = data.join(cluster)
    
    data_mean = data_c.copy()
    for i in range(count_cluster):
        data_mean.loc[data_mean['lable'] == i, 'R'] = data_c[data_c['lable'] == i]['R'].mean()
        data_mean.loc[data_mean['lable'] == i, 'G'] = data_c[data_c['lable'] == i]['G'].mean()
        data_mean.loc[data_mean['lable'] == i, 'B'] = data_c[data_c['lable'] == i]['B'].mean()
    
    psnr_mean = PSNR(data_c, data_mean, width, height, 1)

    data_median = data_c.copy()
    for i in range(count_cluster):
        data_median.loc[data_median['lable'] == i, 'R'] = data_c[data_c['lable'] == i]['R'].median()
        data_median.loc[data_median['lable'] == i, 'G'] = data_c[data_c['lable'] == i]['G'].median()
        data_median.loc[data_median['lable'] == i, 'B'] = data_c[data_c['lable'] == i]['B'].median()
    
    psnr_median = PSNR(data_c, data_median, width, height, 1)
    
    return (count_cluster, psnr_mean, psnr_median)
# def compute_psnr(count_cluster):

#     kmeans = KMeans(n_clusters=count_cluster, init='k-means++', random_state=241).fit(data)
#     cluster = pd.Series(kmeans.labels_, name='lable')
#     data_c = data.join(cluster)
    
#     data_mean = data_c.copy()
#     for i in range(count_cluster):
#         data_mean.loc[data_mean['lable'] == i, ['R', 'G', 'B']] = np.mean(data_c.loc[data_c['lable'] == i, ['R', 'G', 'B']].to_numpy())
    
#     psnr_mean = PSNR(data_c, data_mean, width, height, 1)

#     data_median = data_c.copy()
#     for i in range(count_cluster):
#         data_median.loc[data_median['lable'] == i, ['R', 'G', 'B']] = np.median(data_c.loc[data_c['lable'] == i, ['R', 'G', 'B']].to_numpy())
    
#     psnr_median = PSNR(data_c, data_median, width, height, 1)
    
#     return (count_cluster, psnr_mean, psnr_median)
# def compute_psnr(count_cluster):

#     kmeans = KMeans(n_clusters=count_cluster, init='k-means++', random_state=241).fit(data)
#     cluster = pd.Series(kmeans.labels_, name='lable')
#     data_c = data.join(cluster)
    
#     data_mean = pd.concat([pd.Series(np.zeros(len(data_c)), name='mean'), data_c['lable']], axis=1)
#     for i in range(count_cluster):
#         data_mean.loc[data_mean['lable'] == i, 'mean'] = np.mean(data_c.loc[data_c['lable'] == i, ['R', 'G', 'B']].to_numpy())
    
#     psnr_mean = PSNR(data_c, data_mean, width, height, 1)

#     data_median = pd.concat([pd.Series(np.zeros(len(data_c)), name='median'), data_c['lable']], axis=1)
#     for i in range(count_cluster):
#         data_median.loc[data_median['lable'] == i, 'median'] = np.median(data_c.loc[data_c['lable'] == i, ['R', 'G', 'B']].to_numpy())
    
#     psnr_median = PSNR(data_c, data_median, width, height, 1)
    
#     return (count_cluster, psnr_mean, psnr_median)
# %%time
from multiprocessing import Pool
pool = Pool(4)  # Create a multiprocessing Pool
out = pool.map(compute_psnr, range(1, 20))  # process data_inputs iterable with pool
out<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.1889365017530191, 'fraction_numerical': 0.01645890144137125, 'mean_word_length': 4.915322580645161, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 25, 'https://': 2, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '6', 'dir': 'jupyter-structured-clean-dedup', 'id': '294802', 'license': 'MIT', 'n_tokens_mistral': 4379, 'n_tokens_neox': 4405, 'n_words': 827, 'path': '6_week_1_independent_work.ipynb', 'repo_name': 'Factotum8/coursera_independent_works_ml_introduction', 'size': '10268'}