Top.Mail.Ru
График: 5/2, full-time
Формат: удаленный/офис
Вакансия «1С-программист»

Python. Машинное обучение. Продолжение 3.

Продолжим изучать машинное обучение на Python. На прошлом уроке мы при помощи модели поиска дефектов и модели пикселей пытались исправить испорченную картинку. Кратко напомню суть эксперимента: при помощи модели дефектов ищем дефектный пиксель и при помощи модели пикселя предсказываем, какой там должен быть пиксель. Дефектный пиксель заменяем на тот, что должен быть. В итоге у нас не получилось исправить картинку, вот как выглядит она после такого «исправления»:

Мы решили попробовать сделать следующее:

  • Взять другую модель
  • По-другому сформировать выборку

Для того, чтобы взять другую модель, нам необходимо как-то оценивать ее. Будем оценивать по количеству ошибок. Но сначала проведем небольшой рефакторинг, чтобы не путаться в коде. Во-первых, создадим файл detectors.py, куда поместим вот такой код:

from PIL import Image #Подключим библиотеки для работы с картинками
import random
import pandas as pd

class DefectDetector(object):

    #Конструктор
    #FileOriginal - исходная картинка
    #FileDefected - испорченная исходна картинка
    #W - полуразмер окна (размер окна равно 2W+1)
    def __init__(self, FileOriginal, FileDefected, W):
        self.FileOriginal = FileOriginal
        self.FileDefected = FileDefected
        self.W = W

    #Создание выборки для поиска дефектов
    def CreateDefectionSelection(self):
        imageOriginal = Image.open(self.FileOriginal)
        widthOriginal = imageOriginal.size[0] #Определяем ширину.
        heightOriginal = imageOriginal.size[1] #Определяем высоту.
        imageDefected = Image.open(self.FileDefected)
        widthDefected = imageDefected.size[0] #Определяем ширину.
        heightDefected = imageDefected.size[1] #Определяем высоту.
        pixOriginal = imageOriginal.load() #Выгружаем значения пикселей.
        pixDefected = imageDefected.load() #Выгружаем значения пикселей.
        count=widthOriginal*heightOriginal
        if widthOriginal!=widthDefected:
            print("Не равна ширина оригинала и испорченной картинки")
            return None
        if heightOriginal!=heightDefected:
            print("Не равна высота оригинала и испорченной картинки")
            return None
        ls=[]
        for i in range(self.W,widthOriginal-self.W):
            for j in range(self.W,heightOriginal-self.W):
                if self.ComparePoints(pixOriginal[i,j],pixDefected[i,j]):
                    if random.randint(1,int(count/2000))==1:
                        ls.append(self.CreateDicDefected(pixOriginal, i, j, self.W, 1))
                else:
                    ls.append(self.CreateDicDefected(pixDefected, i, j, self.W, 0))
        return pd.DataFrame(ls)

    def ComparePoints(self, point1, point2):
        if point1[0]!=point2[0]:
            return False
        if point1[1]!=point2[1]:
            return False
        if point1[2]!=point2[2]:
            return False
        return True

    #Создаем словарь строки выборки
    def CreateDicDefected(self, pix, i, j, W, result=-1):
        dic={}
        k=1
        dic["flag"]=random.choice([True,False])
        for x in range(i-W, i+W+1):
            for y in range(j-W,j+W+1):
                if x==i and y==j:
                    continue
                dic["R"+str(k)] = pix[x, y][0]
                dic["G"+str(k)] = pix[x, y][1]
                dic["B"+str(k)] = pix[x, y][2]
                k=k+1
        if result==-1:
            dic["RY"]=pix[i, j][0]
            dic["GY"]=pix[i, j][1]
            dic["BY"]=pix[i, j][2]
        else:
            dic["Y"]=result
        return dic

Как видим, мы оформили алгоритм создания выборки в виде удобного класса, то есть, по сути, используем ООП. Далее, для тестирования тоже создадим отдельный класс ModelTester и разместим его в файле tester.py:

class ModelTester(object):
    #Конструктор
    #model - тестируемая модель
    def __init__(self, model):
        self.model = model

    #произвести тестирование модели
    def test(self,X,Y):
        predicted = self.model.predict(X)
        index = 0
        self.errors_false_yes = 0.0
        self.errors_false_no = 0.0
        self.answers_yes = 0
        self.answers_no = 0
        self.count_items=len(X)
        while index < self.count_items:
            y1 = Y.values[index]
            y2 = predicted[index]
            if y1>0.5:
                self.answers_yes =self.answers_yes+1
                if y1 != y2:
                    self.errors_false_yes=self.errors_false_yes+1
            else:
                self.answers_no=self.answers_no+1
                if y1 != y2:
                    self.errors_false_no=self.errors_false_no+1
            index = index + 1

Теперь осталось сделать запускаемый файл (main.py), который теперь выглядит компактно и лаконично:

from detectors import DefectDetector
from sklearn.ensemble import RandomForestClassifier #случайный лес
from tester import ModelTester

detector=DefectDetector("d:\\rep\\origtest.png","d:\\rep\\origtestspoild.png",2)
selection=detector.CreateDefectionSelection()
selection_train, selection_test = selection.query("flag"), selection.query("~flag")
Y_train = selection_train["Y"]
X_train = selection_train.drop(["Y","flag"], axis=1)
model = RandomForestClassifier()
model.fit(X_train, Y_train)
test=ModelTester(model)
test.test(X_train,Y_train)
print("Кол-во элементов:",test.count_items)
print("Ложных ответов YES:",test.errors_false_yes,"; всего ",test.answers_yes,"; %",test.errors_false_yes/test.answers_yes*100)
print("Ложных ответов NO:",test.errors_false_no,"; всего ",test.answers_no,"; %",test.errors_false_no/test.answers_no*100)

Y_test = selection_test["Y"]
X_test = selection_test.drop(["Y","flag"], axis=1)
test.test(X_test,Y_test)
print("Кол-во элементов:",test.count_items)
print("Ложных ответов YES:",test.errors_false_yes,"; всего ",test.answers_yes,"; %",test.errors_false_yes/test.answers_yes*100)
print("Ложных ответов NO:",test.errors_false_no,"; всего ",test.answers_no,"; %",test.errors_false_no/test.answers_no*100)

 

Теперь кратко объясняю, что делает наша программа. Класс DefectDetector содержит в себе алгоритмы создания обучающей выборки для поиска дефектных пикселей. Он такой-же, как и на предыдущих уроках, за исключением маленького нюанса: в датафрейм выборки добавлено булево поле flag, которое заполняется по рандому. Для чего это надо – чтобы разделить выборку на две – тренировочную и тестовую. Деление происходит путем вызова метода query где мы просто задаем этот флаг в качестве условия. Для одной выборки – прямое условие (туда попадут все записи где flag=True), для второй условие с инверсией, туда, соответственно попадут строк где этот флаг False.

Собственно, командной:

selection_train, selection_test = selection.query("flag"), selection.query("~flag")

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

В данном коде приведен пример обучения модели «Случайный лес». Но мы можем использовать и другие модели. Для этого нам надо только их импортировать. Вот так импортируется случайный лес:

from sklearn.ensemble import RandomForestClassifier

А вот так можно импортировать другие модели:

from sklearn.svm import SVC #модель SVC (метод опорных векторов)
from sklearn.naive_bayes import GaussianNB #Наивный байесовский метод
from sklearn.tree import DecisionTreeClassifier #Решающее дерево
from sklearn.neighbors import KNeighborsClassifier #Алгоритм k-ближайших соседей

 

Теперь исследуем все эти модели, сведя их показатели в сравнительную таблицу:

 

Модель Обучающая выборка, кол-во ошибок, % Тестовая выборка, кол-во ошибок, %
Ошибки YES Ошибки NO Ошибки YES Ошибки NO
Случайный лес 0 0 4.2 3.3
SVC 3.9 10.11 5.7 11.6
Наивный байес 24.9 46.8 27.3 44.2
Решающее дерево 0 0 7.8 11.1
K-ближайших соседей 0.4 20.6 0.7 27.4

 

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

Comments

So empty here ... leave a comment!

Добавить комментарий

Sidebar