В предыдущей части вы узнали, что качество модели Data Science в первую очередь зависит от исходных данных: растет, если у вас больше исходных переменных и уровней градации каждой из них, более равномерно распределены значения каждой из переменных; если у вас меньше пропущенных значений и они менее скоррелированы друг с другом. И наконец, если ваша модель распознает события из прошлого, а не предсказывает будущее.
В третьей, заключительной части статьи я дам ответы по рисункам из предыдущей части. И для дата-сайентистов приведу общий код Python, который использовался для получения всех представленных ниже изображений.
![](https://habrastorage.org/getpro/habr/upload_files/fda/c32/8b6/fdac328b6c9cfd5b985d931585e8010a.png)
Код для изображений
# Импортируем модули
from PIL import Image
import numpy as np
from tqdm import tqdm
import os
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
plt.rcParams['figure.figsize'] = (18,7)
def im_centralized(img):
"""Обрезает рисунок в квадрат
по меньшей стороне"""
width, height = img.size
# рассчитываем точки для обрезки в квадрат
# по наименьшей стороне
if height > width:
left = 0
top = int((height - width) / 2)
right = width
bottom = width + int((height - width) / 2)
else:
left = int((width - height) / 2)
top = 0
right = height + int((width - height) / 2)
bottom = height
# возвращаем изображение, обрезанное в квадрат
# по меньшей стороне
return img.crop((left, top, right, bottom))
Рисунки, связанные с количеством переменных
Далее на рисунках-ответах розовым помечены те точки, которые на рисунках-загадках являются белыми. На гистограмме справа от рисунка красной вертикальной линией отмечен порог между теми точками, которые отображаются как белые, и теми, которые отображаются как черные.
Код для генерации рисунков
def show_images(im_path, share, nw_size):
"""Получает путь к изображению, обрезает его в квадрат
по наименьшей стороне. Приводит к размеру
nw_size * nw_size точек.
share точек делает черными, остальные белыми.
Выводит двухцветное изображение, и рядом с ним -
гистограмму распределения цветов.
Также выводит серое изображение, красит точки,
оказавшиеся белыми, в розовый; рядом с изображением рисует
гистограмму распределения оттенков серого, а также порог,
разделяющий точки, изображенные на первом рисунке
белыми и черными"""
# загружаем изображение
im = Image.open(im_path)
# обрезаем в квадрат по меньшей стороне
im_croped = im_centralized(im)
# переводим в квадрат nw_size * nw_size точек
im_resized = im_croped.resize((nw_size, nw_size))
# конвертируем изображение в тона серого цвета
im_grey = im_resized.convert("L")
# переводим в массив numpy
im_grey = np.array(im_grey)
# выделяем три компоненты цвета
im_red = im_grey.copy()
im_green = im_grey.copy()
im_blue = im_grey.copy()
# значения переменных превращаем в одномерный массив
ravel_points = im_grey.copy().ravel()
# сортируем значения переменных
ravel_points.sort()
# находим порог, отличающий белых от черных
threshold = ravel_points[int(share * len(ravel_points))]
# формируем двухцветное изображение
im_2_colors = im_grey.copy()
im_2_colors[im_2_colors <= threshold] = 0
im_2_colors[im_2_colors > threshold] = 255
# формируем изображение в серых тонах,
# подсвечиваем белые точки (на двухцветном изображении)
# розовым цветом
im_red[im_red > threshold] = 255
im_green[im_green > threshold] = 190
im_blue[im_blue > threshold] = 190
# добавляем дополнительную ось
im_red = im_red[:,:,np.newaxis]
im_green = im_green[:,:,np.newaxis]
im_blue = im_blue[:,:,np.newaxis]
# формируем цветное изображение
im_marked = np.concatenate([im_red, im_green, im_blue],
axis=2) / 255
# получаем параметры гистограммы
hist_height, _ = np.histogram(im_grey.ravel(), 64)
# формируем фигуру
fig = plt.figure(figsize=(18,12))
# создаем сетку для двух графиков
gs = GridSpec(1, 3, figure=fig)
axs0 = fig.add_subplot(gs[0, :-1])
axs1 = fig.add_subplot(gs[0, -1])
# выводим изображение в серых тонах,
# подкрашенное розовым
axs0.imshow(im_marked)
# название изображения
axs0.set_title('Изображение', fontsize=16)
# шрифт меток оси
axs0.tick_params(labelsize=14)
# гистограмма для изображения в серых тонах
axs1.hist(im_grey.ravel(), 64, color='gray');
axs1.set_title('Распределение значений переменных',
fontsize=16);
# рисуем порог
axs1.plot([threshold, threshold], [0, max(hist_height)], 'r',
label='Порог, разделяющий черные и белые точки')
# выводим легенду
axs1.legend(fontsize=14);
axs1.tick_params(labelsize=14)
# аналогично для двухцветного изображения
fig = plt.figure(figsize=(18,12))
gs = GridSpec(1, 3, figure=fig)
axs0 = fig.add_subplot(gs[0, :-1])
axs1 = fig.add_subplot(gs[0, -1])
axs0.imshow(im_2_colors, cmap='gray')
axs0.set_title('Изображение', fontsize=16)
axs0.tick_params(labelsize=14)
axs1.hist(im_2_colors.ravel(), 64, color='k');
axs1.set_title('Распределение значений переменных',
fontsize=16);
axs1.tick_params(labelsize=14)
im_path = 'путь к файлу с изображением'
# задаем размер рисунка
NW_SIZE = 100
# задаем долю значений черного цвета
SHARE = .95
# выводим изображение
show_images(im_path, SHARE, NW_SIZE)
Ответ к рисунку 3 — вертолет. 1 024 переменных, частота модального значения 95%:
![](https://habrastorage.org/getpro/habr/upload_files/874/f53/fc3/874f53fc32b3c54f8cfb9de3b058f4e3.png)
![](https://habrastorage.org/getpro/habr/upload_files/7e9/f1d/f71/7e9f1df71ca7302f7e39c7ca5d2e3919.png)
Ответ к рисунку 4 — лошадь. 2 500 переменных, частота модального значения 95%:
![](https://habrastorage.org/getpro/habr/upload_files/8ea/cb1/53d/8eacb153daf911b5854f0caa4c25ea28.png)
![](https://habrastorage.org/getpro/habr/upload_files/29a/544/fe4/29a544fe461ddfaf27ef9697bf9c05f7.png)
Ответ к рисунку 5 — самолет. 5 000 переменных, частота модального значения 95%:
![](https://habrastorage.org/getpro/habr/upload_files/52b/b5f/c60/52bb5fc60ecd6a037b28e90cbad31dab.png)
![](https://habrastorage.org/getpro/habr/upload_files/77e/34c/766/77e34c7665ea2f653a733689d5d026a3.png)
Ответ к рисунку 6 — автобус. 10 000 переменных, частота модального значения 95%:
![](https://habrastorage.org/getpro/habr/upload_files/1d8/6b7/518/1d86b7518940c3fef05a084d0a407bf2.png)
![](https://habrastorage.org/getpro/habr/upload_files/500/812/bf9/500812bf9bd0e997091f78ff9e50098b.png)
Ответ к рисунку 7 — скоростной поезд. 20 000 переменных, частота модального значения 95%:
![](https://habrastorage.org/getpro/habr/upload_files/89e/c31/c2f/89ec31c2f620e740d18b2c8dbaa0d313.png)
![](https://habrastorage.org/getpro/habr/upload_files/09a/f84/85b/09af8485b0d060861d051ae2b5c31717.png)
Ответ к рисунку 8 — велосипед. 40 000 переменных, частота модального значения 95%:
![](https://habrastorage.org/getpro/habr/upload_files/b7b/9ce/9e1/b7b9ce9e11b4d2492634f9244da00b3b.png)
![](https://habrastorage.org/getpro/habr/upload_files/5d9/a82/337/5d9a823376c060cbc3aa4060f057328e.png)
Ответ к рисунку 9 — слон. 100 000 переменных, частота модального значения 95%:
![](https://habrastorage.org/getpro/habr/upload_files/a4a/629/f1d/a4a629f1de9c6a99bdee06a1d97ed734.png)
![](https://habrastorage.org/getpro/habr/upload_files/374/cac/f43/374cacf43924289994c1a3a66917e740.png)
Ответ к рисунку 10 — рыба. 250 000 переменных, частота модального значения 95%:
![](https://habrastorage.org/getpro/habr/upload_files/84d/30d/18a/84d30d18ae39d8815f9786f1110cf087.png)
![](https://habrastorage.org/getpro/habr/upload_files/b5e/e4f/248/b5ee4f2488467817cb7d65bb5ee75c12.png)
Рисунки, связанные с частотой модального значения
Для их генерации использовался тот же код, что я для рисунков, связанных с числом переменных.
Ответ к рисунку 11 — лягушка. 10 000 переменных, частота модального значения 95%:
![](https://habrastorage.org/getpro/habr/upload_files/167/84b/8c1/16784b8c112a2f23f99c1cb25dd27ed0.png)
![](https://habrastorage.org/getpro/habr/upload_files/bc6/6d3/601/bc66d3601cbf76b2b84fa218c8ebd567.png)
Ответ к рисунку 12 — парусный корабль. 10 000 переменных, частота модального значения 90%:
![](https://habrastorage.org/getpro/habr/upload_files/53c/169/223/53c169223a46bd41604fbc3c60d77389.png)
![](https://habrastorage.org/getpro/habr/upload_files/a73/8b0/a77/a738b0a774edd1a22674adb8555f09cc.png)
Ответ к рисунку 13 — автомобиль. 10 000 переменных, частота модального значения 80%:
![](https://habrastorage.org/getpro/habr/upload_files/26b/4c6/4ed/26b4c64ed64196641913062c20c49c6d.png)
![](https://habrastorage.org/getpro/habr/upload_files/917/262/e3d/917262e3d1c8f264e9d92d584b9becd4.png)
Ответ к рис. 14 — тигр. 10 000 переменных, частота модального значения 70%.
![](https://habrastorage.org/getpro/habr/upload_files/771/4ae/697/7714ae697ecf5ab38538710bb90884e8.png)
![](https://habrastorage.org/getpro/habr/upload_files/cb5/f9b/3da/cb5f9b3da977e740d07d347a156213fc.png)
Ответ к рис. 15 — трактор. 10 000 переменных, частота модального значения 60%:
![](https://habrastorage.org/getpro/habr/upload_files/a2e/913/baf/a2e913baf25217bab63727f16dfa4719.png)
![](https://habrastorage.org/getpro/habr/upload_files/7d8/099/cfa/7d8099cfae3f714ec6089f277744cd0e.png)
Ответ к рисунку 16 — птица. 10 000 переменных, частота модального значения 50%:
![](https://habrastorage.org/getpro/habr/upload_files/a91/bd8/30c/a91bd830c9d1733010ea1d4ea836feba.png)
![](https://habrastorage.org/getpro/habr/upload_files/0c3/594/5fc/0c35945fcb1b2c7f23d5a1aa96e86d79.png)
Ответ к рисунку 17 — змея. 10 000 переменных, частота модального значения 95%:
![](https://habrastorage.org/getpro/habr/upload_files/f9b/ef2/72d/f9bef272db403a79ece30674fe615565.png)
![](https://habrastorage.org/getpro/habr/upload_files/c1a/0a5/1b3/c1a0a51b3ccc64df122c6ec001ba2771.png)
Рисунок для демонстрации трех градаций переменных
Далее справа от рисунка я приведу гистограмму. На ней синей вертикальной чертой изображена граница между точками, раскрашенными черным и серым цветом. Красной вертикальной чертой – между раскрашенными серым и белым цветом.
Код для генерации изображений
def images_3_colors(im_path, nw_size):
"""Получает путь к изображению, обрезает его в квадрат
по наименьшей стороне. Приводит к размеру
nw_size * nw_size точек.
Делает 1/3 точек черными, 1/3 серыми, 1/3 белыми.
Выводит трехцветное изображение, и рядом с ним
гистограмму распределения цветов.
"""
# загружаем изображение
im = Image.open(im_path)
# обрезаем в квадрат по меньшей стороне
im_croped = im_centralized(im)
# переводим в квадрат nw_size * nw_size точек
im_resized = im_croped.resize((nw_size, nw_size))
# конвертируем изображение в тона серого цвета
im_grey = im_resized.convert("L")
# переводим в массив numpy
im_grey = np.array(im_grey)
# значения переменных превращаем в одномерный массив
ravel_points = im_grey.copy().ravel()
# сортируем значения переменных
ravel_points.sort()
# находим 2 порога, делящие градации цвета
# на 3 части
threshold1 = ravel_points[int(1/3 * len(ravel_points))]
threshold2 = ravel_points[int(2/3 * len(ravel_points))]
# формируем трехцветное изображение
im_3_colors = im_grey.copy()
im_3_colors[im_3_colors <= threshold1] = 0
im_3_colors[im_3_colors > threshold2] = 255
im_3_colors[(im_3_colors > 0)&(
im_3_colors <255)] = 127
# получаем параметры гистограммы
hist_height, _ = np.histogram(im_grey.ravel(), 64)
# формируем фигуру
fig = plt.figure(figsize=(18,12))
# создаем сетку для двух графиков
gs = GridSpec(1, 3, figure=fig)
axs0 = fig.add_subplot(gs[0, :-1])
axs1 = fig.add_subplot(gs[0, -1])
# выводим изображение в серых тонах
axs0.imshow(im_grey, cmap='gray')
# название изображения
axs0.set_title('Изображение', fontsize=16)
# шрифт меток оси
axs0.tick_params(labelsize=14)
# гистограмма для изображения в серых тонах
axs1.hist(im_grey.ravel(), 64, color='gray');
axs1.set_title('Распределение значений переменных',
fontsize=16);
# рисуем пороги
axs1.plot([threshold1, threshold1], [0, max(hist_height)], 'b',
label='Порог, разделяющий черные и серые точки')
axs1.plot([threshold2, threshold2], [0, max(hist_height)], 'r',
label='Порог, разделяющий серые и белые точки')
# выводим легенду
axs1.legend(fontsize=14);
axs1.tick_params(labelsize=14)
# формируем фигуру
fig = plt.figure(figsize=(18,12))
# создаем сетку для двух графиков
gs = GridSpec(1, 3, figure=fig)
axs0 = fig.add_subplot(gs[0, :-1])
axs1 = fig.add_subplot(gs[0, -1])
# выводим трехцветное изображение
axs0.imshow(im_3_colors, cmap='gray')
# название изображения
axs0.set_title('Изображение', fontsize=16)
# шрифт меток оси
axs0.tick_params(labelsize=14)
# гистограмма для изображения в серых тонах
axs1.hist(im_3_colors.ravel(), 64, color='gray');
axs1.set_title('Распределение значений переменных',
fontsize=16);
axs1.tick_params(labelsize=14)
im_path = 'путь к файлу с изображением'
# задаем размер изображения
NW_SIZE = 100
# выводим изображение
images_3_colors(im_path, NW_SIZE)
Ответ к рисунку 18 — овца. 10 000 переменных, три градации с частотой по 33% каждая:
![](https://habrastorage.org/getpro/habr/upload_files/c4e/efd/0fc/c4eefd0fc919368d0af252e573a7c7e3.png)
![](https://habrastorage.org/getpro/habr/upload_files/948/1d6/7d3/9481d67d370811508a52394aa6183c3d.png)
Рисунки с некоррелированными пропусками переменных
Справа от рисунка ниже по-прежнему представлена гистограмма. Самый высокий столбик на ней — число пропусков, замененных средним фоном рисунка. Чем больше пропусков, тем выше этот столбик по сравнению со всеми остальными.
Код для генерации изображений
def show_noised(im_path, share, nw_size):
"""Изображение по адресу im_path обрезает
в квадрат по меньшей стороне, переводит
в градации серого, а затем удаляет из него
долю точек, равную share, путем замены их
истинного цвета на средний цвет всего рисунка
Слева выводит зашумленное изображение,
справа - гистограмму распределения цветов"""
im = Image.open(im_path)
# обрезаем в квадрат по наименьшей стороне
im_croped = im_centralized(im)
# переводим в квадрат nw_size * nw_size точек
im_resized = im_croped.resize((nw_size, nw_size))
# конвертируем файл в тона серого
im_grey = im_resized.convert("L")
# конвертируем файл в numpy
im_grey = np.array(im_grey)
# формируем файл шума того же размера,
# что и текущий
noise = np.random.rand(
im_grey.shape[0], im_grey.shape[1])
# формируем зашумленный файл
im_noised = im_grey.copy()
# для точек, которым соответствуют значения
# в случайном файле, превосходящие 1 - share, заменяем
# истинный цвет на цвет фона
im_noised[noise > 1 - share] = im_grey.mean()
# создаем фигуру
fig = plt.figure(figsize=(18,12))
# создаем сетку для двух графиков
gs = GridSpec(1, 3, figure=fig)
axs0 = fig.add_subplot(gs[0, :-1])
axs1 = fig.add_subplot(gs[0, -1])
# выводим изображение в серых тонах,
# подкрашенное розовым
axs0.imshow(im_noised, cmap='gray')
# название изображения
axs0.set_title('Изображение', fontsize=16)
# шрифт меток оси
axs0.tick_params(labelsize=14)
# гистограмма для изображения в серых тонах
axs1.hist(im_noised.ravel(), 64, color='gray');
axs1.set_title('Распределение значений переменных',
fontsize=16);
axs1.tick_params(labelsize=14)
im_path = 'путь к файлу с изображением'
# задаем размер рисунка
NW_SIZE = 100
# задаем долю шума
SHARE = .5
# выводим изображение
show_noised(im_path, SHARE, NW_SIZE)
Ответ к рисунку 19 — велосипедистка. 10 000 переменных, доля нескоррелированных пропусков 20%:
![](https://habrastorage.org/getpro/habr/upload_files/f4d/288/f74/f4d288f74ec8a30423bb11a7ee5891e5.png)
Ответ к рисунку 20 — обезьяна. 10 000 переменных, доля нескоррелированных пропусков 40%:
![](https://habrastorage.org/getpro/habr/upload_files/3cf/115/5ef/3cf1155effda04b24a73e2fb349b74c0.png)
Ответ к рисунку 21 — стиральная машина. 10 000 переменных, доля нескоррелированных пропусков 50%:
![](https://habrastorage.org/getpro/habr/upload_files/3f0/8fe/79e/3f08fe79e8bed28499287b3de022d930.png)
![](https://habrastorage.org/getpro/habr/upload_files/a2e/928/b30/a2e928b301937bb761f1caf081b3cc22.png)
Ответ к рисунку 22 — лев. 10 000 переменных, доля нескоррелированных пропусков 60%:
![](https://habrastorage.org/getpro/habr/upload_files/0e0/00d/eb3/0e000deb37179be88a0eb44b0c6e4919.png)
![](https://habrastorage.org/getpro/habr/upload_files/24a/6b1/845/24a6b1845137abdd24f769bb42358aad.png)
Ответ к рис. 23 — танк. 10 000 переменных, доля нескоррелированных пропусков 70%:
![](https://habrastorage.org/getpro/habr/upload_files/94f/737/3da/94f7373da198c3773dc445ef89648c1e.png)
![](https://habrastorage.org/getpro/habr/upload_files/1f6/506/10d/1f650610d9857367a0758b4e84457710.png)
Ответ к рисунку 24 — чашка кофе. 10 000 переменных, доля нескоррелированных пропусков 80%:
![](https://habrastorage.org/getpro/habr/upload_files/f7a/434/56a/f7a43456a2c0ce1182f5f95925b9c3ff.png)
![](https://habrastorage.org/getpro/habr/upload_files/4aa/15e/bea/4aa15ebea48cbd1be5834efc5ccf3f0e.png)
Ответ к рисунку 25 — чайник. 10 000 переменных, доля нескоррелированных пропусков 90%:
![](https://habrastorage.org/getpro/habr/upload_files/de8/385/ea6/de8385ea642622be0de714a4a4e652cc.png)
![](https://habrastorage.org/getpro/habr/upload_files/6bd/e55/8a7/6bde558a76565dcd32c86c2fada6d1a1.png)
Рисунки со скоррелированными пропусками переменных
Слева показан зашумленный рисунок, справа — матрица шума. Они имеют одинаковый размер. Матрица шума накладывается на рисунок. Там, где в матрице шума белые точки, на рисунке оригинальное изображение заменено серым цветом. Там, где в матрице шума черные точки, сохранено оригинальное изображение.
Код для генерации изображений
def show_correlated_noise(im_path, nw_size, only_left=True):
"""Обрезает рисунок в квадрат по наименьшему
размеру, конвертирует в градации серого цвета,
если only_left=True выводит только пикселизированное
изображение.
Если only_left=False, то функция
выводит слева исходный рисунок с сеткой,
соответствующей числу пикселей справа,
а справа - пикселизированное изображение"""
# загружаем изображение
im = Image.open(im_path)
# обрезаем в квадрат по наименьшей стороне
im_croped = im_centralized(im)
# конвертируем изображение в тона серого цвета
im_grey = im_croped.convert("L")
# переводим в квадрат nw_size * nw_size точек
im_resized = im_grey.resize((nw_size, nw_size))
# конвертируем файл в numpy
im_grey1 = np.array(im_resized)
# формируем файл шума того же размера,
# что и изображение
noise = np.random.rand(
im_grey1.shape[0], im_grey1.shape[1])
# среднее значение
# цвета всего серому изображению
gray_mean = im_grey1.mean()
# формируем файл со скоррелированным шумом
noise_correlated = 0 * noise
# вероятность шума изменяется вдоль
# ширины рисунка по логистическому
# закону: слева почти все точки зашумлены,
# справа шума почти нет
for i in tqdm(range(im_grey1.shape[1])):
# noise_column - одномерный вектор
# для одновременного вычисления степени зашумленности
# всех точек с одинаковой координатой по ширине
# копируем нескоррелированный шум
# с данной координатой по ширине
noise_column = noise[:, i].copy()
# в зависимости от координаты по ширине с помощью
# логистической кривой устанавливаем точки, в которых
# шум равен 1: чем левее, тем таких точек больше
noise_column[noise_column < 1 / (
1 + np.exp((i / im_grey1.shape[1] - .5)/.05)) ] = 1
# формируем очередной столбец для скоррелированного шума
noise_correlated[:, i] = noise_column
# формируем зашумленное изображение
im_grey_noised = im_grey1.copy()
im_grey_noised[noise_correlated == 1] = gray_mean
if only_left == True:
# создаем фигуру
fig = plt.figure(figsize=(8, 8))
plt.imshow(im_grey1, cmap='gray');
plt.title('Исходное изображение'.format(
nw_size, nw_size),
fontsize=16);
plt.tick_params(labelsize=14)
else:
# создаем фигуру
fig = plt.figure(figsize=(18, 9))
# создаем сетку для двух изображений
gs = GridSpec(1, 2, figure=fig)
axs0 = fig.add_subplot(gs[0, 0])
axs1 = fig.add_subplot(gs[0, 1])
# выводим зашумленное изображение в серых тонах
axs0.imshow(im_grey_noised, cmap='gray')
# название изображения
axs0.set_title('Зашумленное изображение', fontsize=16)
# шрифт меток оси
axs0.tick_params(labelsize=14)
# выводим файл скоррелированного шума
axs1.imshow(noise_correlated == 1, cmap='gray');
axs1.set_title('Шум: зашумленные точки показаны белым цветом'.format(
nw_size, nw_size),
fontsize=16);
axs1.tick_params(labelsize=14)
im_path = 'путь к файлу с изображением'
# задаем размер рисунка
NW_SIZE = 100
# выводим зашумленное изображение
show_correlated_noise(im_path, NW_SIZE, only_left=False)
# выводим исходное изображение
show_correlated_noise(im_path, NW_SIZE, only_left=True)
Ответ к рисунку 26 — две девушки. 10 000 переменных, доля скоррелированных пропусков 50%:
![](https://habrastorage.org/getpro/habr/upload_files/c27/5a9/547/c275a954717757201d52c7a6b4f3bbed.png)
![](https://habrastorage.org/getpro/habr/upload_files/fb9/f30/a37/fb9f30a37fffde0d540f7897c0c098ff.png)
Ответ к рисунку 27 — две девушки. 1 000 000 переменных, доля скоррелированных пропусков 50%:
![](https://habrastorage.org/getpro/habr/upload_files/273/fbd/202/273fbd202e4a7bb1d4434a15c99771ae.png)
![](https://habrastorage.org/getpro/habr/upload_files/c78/8b4/23c/c788b423cead3925042741c404cbb8b3.png)
Вот и всё!
В заключение пожелаем представителям бизнес-подразделений снабжать дата-сайентистов качественными данными, а дата-сайентистам — строить на них модели с точностью 100 и более процентов (ну это я погорячился :)). Для удобства — ссылки на первую и вторую часть этой статьи.