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

Код для генерации картинок
# Импортируем модули
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_resized_photo(im_path, nw_size, only_left=True):
    """Обрезает рисунок в квадрат по наименьшему
    размеру, конвертирует в градации серого цвета.
    Если only_left=True, выводит только пикселизированное
    изображение.
    Если only_left=False, то функция
    выводит слева исходный рисунок с сеткой, 
    соответствующей числу пикселей справа,
    а справа - пикселизированное изображение"""
    
    def show_grid(axs, n_grid, step, bias):        
        """Наносит сетку на изображение, которое
        размещено в осях axs, n_grid - количество 
        делений сетки по высоте и ширине, step - шаг сетки,  
        bias - смещение, необходимое для центрирования
        единичного квадрата сетки"""        
        
        # n_grid * step - это размер рисунка
        
        for i in range(1, n_grid + 1):
            # вертикальные линии сетки:
            # кол-во линий: (n_grid-1) 
            # с шагом step,
            # смещенных на bias для центрирования
            # единичного квадрата
            axs.plot([bias, n_grid * step + bias], 
                     [i * step + bias, i * step + bias], 
                     'm', linewidth=.5)
            
            # аналогично - горизонтальные линии сетки
            axs.plot([i * step + bias, i * step + bias],
                     [bias, n_grid * step + bias], 
                     'm', linewidth=.5)

        # устанавливаем лимиты по осям 
        # (чтобы рисунок с сеткой занял все
        # пространство осей)
        axs.set_xlim(bias, n_grid * step + bias)
        axs.set_ylim(n_grid * step + bias, bias)    
    
    
    # загружаем изображение
    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))
    
    if only_left == True:
        # создаем фигуру
        fig = plt.figure(figsize=(8, 8))
        plt.imshow(im_resized, 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, cmap='gray')

        # определяем шаг сетки
        grid_step = im_grey.size[0] / nw_size    

        # наносим сетку
        show_grid(axs0, nw_size, grid_step, 0)

        # название изображения
        axs0.set_title('Исходное изображение', fontsize=16)
        # шрифт меток оси
        axs0.tick_params(labelsize=14)

        # выводим изображение nw_size x nw_size пикселей
        axs1.imshow(im_resized, cmap='gray');

        # наносим сетку
        show_grid(axs1, nw_size, 1, -0.5)

        axs1.set_title('Изображение {}х{} пикселей'.format(
            nw_size, nw_size), 
                       fontsize=16);

        axs1.tick_params(labelsize=14)

impath = 'Путь к файлу с изображением'

# формируем изображение – загадку
show_resized_photo(im_path, 12, only_left=True)

#формируем изображение – отгадку
show_resized_photo(im_path, 12, only_left=False)

Ответы к задачам из прошлой части

Ответ к рисунку 1: слева собака, справа кошка. 

Ответ к рисунку 2: слева кошка, справа собака. 

 Ответ к рисунку 3: слева кошка, справа собака. 

Ответ к рисунку 4: слева собака, справа кошка. 

Ответ к рисунку 5: слева собака, справа кошка.

Ответ на главный вопрос

А теперь хотите узнать ответ на самый важный вопрос предыдущей части статьи: от чего зависит качество моделей, которые делают для вас дата-сайентисты?

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

Но если картинка окружена рамкой, то изображение рамки никак не влияет на картинку. Увеличивая детализацию изображения рамки, нельзя улучшить качество распознавания картинки. Так же и с моделью: дополнительная информация о ценах на недвижимость, например, в Калифорнии не поможет нам при уточнении цены квартиры в Москве.

И вот ответ. На качество модели влияют следующие параметры:

  • Количество переменных, доступных аналитику (чем больше, тем лучше).

  • Количество возможных значений каждой из переменных (чем больше, тем лучше).

  • Распределение частот значений переменных (если каждое из значений переменной примерно равновероятно, это значительно лучше, чем если есть одно доминирующее значение).

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

  • Корреляция (наличие связи) пропусков между различными переменными (чем сильнее корреляция, тем хуже).

  • Что требуется предсказать: уже состоявшееся событие или событие в будущем (уже произошедшее событие гораздо легче предсказать).

  • Наконец, последнее, что влияет на качество модели — это использованный аналитиком метод моделирования.

Игра продолжается!

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

На этой картинке (в исходном качестве) 900 000 переменных. Каждая из них принимает одно из 256 значений. И мы легко можем понять, что на картинке изображено.

Рис. 1
Рис. 1

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

Код обработки
# Импортируем модули
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 show_parrot(im_path):
    """Функция выводит изображение
    попугая в серых тонах и соответствующее
    ей распределение переменных"""
    
    im = Image.open(im_path)
    # первый раз уменьшаем размер
    im_resized = im.resize((1036, 1295))
    # обрезаем изображение
    im_croped = im_resized.crop((18, 0, 1018, 1200))
    # второй раз уменьшаем размер
    im_resized2 = im_croped.resize((500, 600))
    # конвертируем в оттенки серого цвета
    im_grey = im_resized2.convert('L')
    # конвертируем в массив numpy
    im_grey = np.array(im_grey)
    
    # создаем фигуру
    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.tick_params(labelsize=14)

im_path = 'путь к файлу с изображением'
# выводим изображение
show_parrot(im_path)

Вот результат обработки:

Рис. 2
Рис. 2

Справа от рисунка приведена гистограмма. По оси х отложены варианты значений переменных (от 0 до 255). Высота столбика показывает, какое количество переменных (точек изображения) имеет значение 0, какое 1, какое 2 и так далее до 255. Мы видим, что бо́льшая часть значений сосредоточена в диапазоне от 25 до 100, что соответствует различным оттенкам серого цвета.

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

  • количество комнат (от 1 до 5);

  • метро (более 300 станций);

  • район (12 округов, около 150 районов);

  • тип квартиры (2 градации – новостройка или вторичка);

  • общая площадь (непрерывная переменная);

  • жилая площадь (непрерывная переменная);

  • площадь кухни (непрерывная переменная);

  • этаж (приблизительно 25 градаций);

  • количество этажей в доме (приблизительно 25 градаций);

  • наличие парковки (да/нет);

  • квартира в собственности у нынешнего владельца более 3 лет (да/нет);

  • есть фото (да/нет);

  • квартира свободна (да/нет);

  • возможна ипотека (да/нет);

  • материал дома (10 градаций);

  • балкон (5 градаций);

  • ремонт (5 градаций);

  • вид из окна (3 градации);

  • санузел (3 градации).

Мы видим, что некоторые параметры (метро, район) имеют много градаций, в то время как другие — всего две (тип квартиры, наличие парковки, квартира в собственности более 3 лет, фото, квартира свободна, ипотека). 

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

Фактор 1. Влияние количества переменных на качество модели

Пусть все переменные будут иметь только два возможных значения – 0 или 1. При этом одно значение переменной (например, 0) будет встречаться гораздо чаще: 0 в 95% случаев, а 1 в 5% случаев. Давайте посмотрим, сколько вам потребуется таких переменных, чтобы определить, что изображено на картинке. На каждой из представленных в этом разделе картинок изображен предмет, отличный от тех, что изображены на других картинках.

Рис. 3. На этой картинке 1 000 переменных. Что на ней изображено?
Рис. 3. На этой картинке 1 000 переменных. Что на ней изображено?
Рис. 4. Вот тут 2 500 переменных. Что вы видите?
Рис. 4. Вот тут 2 500 переменных. Что вы видите?
Рис. 5. Здесь 5 000 переменных. Стало ли вам легче понять?
Рис. 5. Здесь 5 000 переменных. Стало ли вам легче понять?
Рис. 6. 10 000 переменных. Кажется, что-то проявляется, но что?
Рис. 6. 10 000 переменных. Кажется, что-то проявляется, но что?
Рис. 7. 20 000 переменных. Что вы видите?
Рис. 7. 20 000 переменных. Что вы видите?
Рис. 8. 40 000 переменных. Вы догадались?
Рис. 8. 40 000 переменных. Вы догадались?
Рис. 9. 100 000 переменных. Наконец-то наступила ясность.
Рис. 9. 100 000 переменных. Наконец-то наступила ясность.
Рис. 10. 250 000 переменных. Теперь всё понятно.
Рис. 10. 250 000 переменных. Теперь всё понятно.

Фактор 2. Влияние распределения переменных на качество модели

Теперь пойдем другим путем. Зафиксируем количество переменных — 10 000 — но будем изменять частоту, с которой встречается наиболее частое (так называемое модальное) значение переменной.

Рис. 11. Частота модального значения – 95%. Что вы видите?
Рис. 11. Частота модального значения – 95%. Что вы видите?
Рис. 12. Сократим частоту модального значения до 90%. Кажется, что-то начало проглядываться.
Рис. 12. Сократим частоту модального значения до 90%. Кажется, что-то начало проглядываться.
Рис. 13. Частота модального значения – 80%. Совсем другое дело.
Рис. 13. Частота модального значения – 80%. Совсем другое дело.
Рис. 14. Снизим частоту модального значения до 70%. Вам видно?
Рис. 14. Снизим частоту модального значения до 70%. Вам видно?
Рис. 15. 60% модальных значений – всё хорошо видно.
Рис. 15. 60% модальных значений – всё хорошо видно.
Рис. 16. 50% / 50% — всё отлично понятно.
Рис. 16. 50% / 50% — всё отлично понятно.

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

Рис. 17. Доля единиц – 95%. Вам что-нибудь понятно?
Рис. 17. Доля единиц – 95%. Вам что-нибудь понятно?

Фактор 3. Влияние количества градаций переменных на качество модели

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

Рис. 18. Стало еще лучше, не правда ли?
Рис. 18. Стало еще лучше, не правда ли?

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

Фактор 4. Влияние некоррелированных пропусков переменных

Часто переменные имеют пропуски (отсутствие данных). Посмотрим, как это мешает распознаванию изображения. 

Предположим сначала, что пропуски переменных никак не связаны между собой. Зафиксируем 10 000 переменных. Пусть количество градаций одной переменной – 256. Пропуски мы будем заполнять цветом фона.

Рис. 19. На этом рисунке пропущено 20% значений. Всё хорошо видно.
Рис. 19. На этом рисунке пропущено 20% значений. Всё хорошо видно.
Рис. 20. Здесь количество пропусков – 40%. По-прежнему нет проблем.
Рис. 20. Здесь количество пропусков – 40%. По-прежнему нет проблем.
Рис. 21. Доля пропусков уже 50%. Это мешает вам распознать предмет?
Рис. 21. Доля пропусков уже 50%. Это мешает вам распознать предмет?
Рис. 22. Количество пропусков уже 60%. Можете разглядеть изображение?
Рис. 22. Количество пропусков уже 60%. Можете разглядеть изображение?
Рис. 23. Пропущено 70% значений. Что вы видите?
Рис. 23. Пропущено 70% значений. Что вы видите?
Рис. 24. 80% пропусков. Но еще можно что-то разглядеть.
Рис. 24. 80% пропусков. Но еще можно что-то разглядеть.
Рис. 25. Идем дальше: 90% пропусков. Что здесь изображено?
Рис. 25. Идем дальше: 90% пропусков. Что здесь изображено?

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

Фактор 5. Влияние скоррелированных пропусков переменных

Но чаще в жизни встречается другая ситуация: пропуски хотя бы частично скоррелированы (связаны) между собой. Например, мы опрашиваем людей по телефону. Анкета длинная, состоит из 40 вопросов. Со временем человеку надоедает, и он прекращает отвечать на вопросы. Это означает, что если респондент ответил только на 15 вопросов, то для него значения всех оставшихся 25 переменных будут пропусками. У другого, допустим, терпения хватило на 30 ответов — значения 10 последних переменных пропущены и так далее.

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

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

Вернемся к игре. Мы по-прежнему имеем 10 000 переменных, из которых 50% — шум. Когда пропуски в переменных не были скоррелированы, мы легко могли разобрать изображение при таком уровне шума. А сейчас вы можете сказать, что/кто изображен(о) в левой части рисунка?

Рис. 26. 10 000 переменных со скоррелированным шумом. Что/кто изображен(о) слева?
Рис. 26. 10 000 переменных со скоррелированным шумом. Что/кто изображен(о) слева?

Возможно, вам не хватает деталей? Давайте вместо 10 000 переменных возьмем 1 000 000 переменных. Изображение в правой части рисунка стало гораздо лучше. А в левой?

Рис. 27. 1 000 000 переменных со скоррелированным шумом. Что/кто изображен(о) слева?
Рис. 27. 1 000 000 переменных со скоррелированным шумом. Что/кто изображен(о) слева?

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

Фактор 6. Что требуется предсказать: уже состоявшееся событие или событие в будущем

Если событие уже произошло в прошлом, то иногда мы можем определить его с точностью, близкой к 100%. Если же событие произойдет в будущем, мы не можем быть настолько уверены. Вот два примера.

Взгляните на этот лист бумаги. Что на нем нарисовал ребенок?
Взгляните на этот лист бумаги. Что на нем нарисовал ребенок?
А что ребенок нарисует на этом листе?
А что ребенок нарисует на этом листе?

Мы можем относительно точно определить, что ребенок уже нарисовал. Но что он собирается нарисовать, мы можем только угадать с определенной степенью вероятности. Чем лучше мы знаем самого́ ребенка, его интересы и вкусы, окружающую его обстановку, тем точнее можем угадать, что он собирается нарисовать. Но мы никогда не сможем предсказать сюжет будущей картинки с точностью 100%. Мы имеем дело с фундаментальной неопределенностью будущего.

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

Аналогична ситуация и в бизнесе. Мы никогда не сможем точно сказать, отдаст клиент кредит или не отдаст. Наша оценка может иметь только вероятностный характер. Даже сам клиент не знает, вернет ли он кредит. Если он мошенник, то точно знает, что не намерен возвращать деньги. Но честный клиент тоже может не вернуть кредит по не зависящим от него обстоятельствам. И пока эти обстоятельства не наступили, мы можем только оценить их вероятность, но не предсказать наверняка.

Даже если срок возврата кредита уже прошел и мы знаем точный ответ, при построении модели возврата кредита мы можем пользоваться только теми данными, которыми располагали на момент выдачи кредита. Поэтому наша модель априори не может иметь 100% точность.

А существуют ли модели со 100% точностью?

Да, такие модели существуют — например, наборы картинок для распознавания, такие как Mnist, Cifar-10, ImageNet.

Для всех этих наборов построены модели, которые распознают их с точностью, близкой к 100%. При этом все наборы обладают следующими характеристиками:

  • Большое количество переменных (Mnist – 784, Cifar – 3072, ImageNet – порядка 750 000).

  • Большое количество возможных значений каждой из переменных (256).

  • Близкое к равномерному распределение частот значений переменных.

  • Отсутствие пропусков в переменных (нулевой уровень шума).

  • Отсутствие корреляции пропусков между различными переменными.

  • На картинках запечатлены уже состоявшиеся события.

Чем ближе ваша задача к указанным параметрам, тем больше вероятность построить модель с точностью 100%. И наоборот, если указанные условия не выполняются, то и достижение точности 100% представляется мало достижимым.

Подведем итоги

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

  • Переменных много.

  • Каждая из переменных имеет много градаций. 

  • Значения каждой из переменных распределены между градациями примерно равномерно.

  • Пропусков в переменных мало.

  • Пропуски в различных переменных не скоррелированы между собой.

О том, что важно моделировать уже произошедшие события, я упоминать не буду. Предсказание будущего — зачастую именно то, что вам нужно от дата-сайентиста. Но не стоит удивляться, если такая модель не будет иметь 100% точности.

А где ответы?

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

Комментарии (7)


  1. aamonster
    26.07.2023 10:11
    -1

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


    1. rvishnevsky Автор
      26.07.2023 10:11
      +1

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


      1. aamonster
        26.07.2023 10:11
        -1

        Если выкидываем из базы исходную картинку с котом и её вариации – какая окажется максимально похожей? Где на картинке кот в другой позе или собака в той же позе? На крайняк можно и постановочную фотку сделать, оптимизируя по нашей модели)


  1. Kragius
    26.07.2023 10:11

    Я все еще уверен что на третьей картинке слева не кот, а собака. Я в породах не очень, но похоже на йорка.

    Я вот вижу собачку


    1. rvishnevsky Автор
      26.07.2023 10:11
      +1

      А модель уверена, что клиент хороший (а на самом деле он плохой). Именно так и возникают ошибки классификации.


    1. aamonster
      26.07.2023 10:11

      Я тут вообще вижу осла :-)
      Мордой к нам, уши наверх торчат, морда вниз и чуть влево.


  1. diakin
    26.07.2023 10:11

    Имя собаки: Мотя
    Порода собаки: кошка

    Ну кончились в ветклинике бланки для кошек.