Top.Mail.Ru

Python. Машинное обучение.

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

И попробуем загнать в модель машинного обучения окружения точек, точнее, цвета тех точек, которые находятся вокруг данной точки. Напомню, что цвет точки – это числа от 0 до 255, для каждой составляющих RGB (Red, Blue, Green).

Давайте изобразим это графически:

В центре колец из циферок – искомая точка, а точки, ее окружающие, подаются на вход модели машинного обучения. Если размер скользящего окна (вот этого квадратика из циферок) равен 5, то входной элемент состоит из 24 точек (сама эта точке в него не входит). Соответственно, на вход подается 72 числа. На выходе – одно число, это составляющая R, G или B анализируемой точки. Соответственно, нам потребуется три модели, для каждого из цветов RGB, которые мы будет обучать и использовать отдельно.

Для проведения данного эксперимента нам понадобятся следующие знания:

 

 

Для начала создадим экспериментальную программу, которая предскажет красную составляющую цвета точки. В качестве обучающей выборки используем несколько рандомных точек, в качестве тестовой – другие несколько рандомных точек. В качестве модели машинного обучения был выбран случайный лес (RandomForestClassifier). Итак, вот программа на Python:

import random
import pandas as pd
import numpy as np
from sklearn.ensemble import RandomForestClassifier
from PIL import Image, ImageDraw #Подключим библиотеки для работы с картинками

image = Image.open("d:\\1\\DSCN1128.jpg") #Открываем изображение. 
draw = ImageDraw.Draw(image) #Создаем инструмент для рисования. 
width = image.size[0] #Определяем ширину. 
height = image.size[1] #Определяем высоту. 	
pix = image.load() #Выгружаем значения пикселей.
count=width*height
ls_train=[]
ls_test=[]
W=2 # Полуразмер окна
for i in range(W,width-W):
    for j in range(W,height-W):
        dic={}
        k=1
        if random.randint(1,int(count/2000))==1:
            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
            dic["RY"]=pix[i, j][0]
            dic["GY"]=pix[i, j][1]
            dic["BY"]=pix[i, j][2]
            if random.randint(1,10)>5:
                ls_train.append(dic)
            else:
                ls_test.append(dic)
data_train=pd.DataFrame(ls_train)
data_test=pd.DataFrame(ls_test)

Y = data_train["RY"]
X = data_train.drop(["GY", "BY"], axis=1)

count_items_trian=data_train.count(axis=0)["RY"]
count_items_test=data_test.count(axis=0)["RY"]

#Обучим модель
model = RandomForestClassifier()
model.fit(X, Y)

predicted_train = model.predict(X)

Y_test = data_test["RY"]
X_test = data_test.drop(["GY", "BY"], axis=1)

predicted_test = model.predict(X_test)

index=0
error=0.0
while index<count_items_trian:
    y1=Y[index]
    y2=predicted_train[index]  
    error=error+abs(y1-y2)
    index=index+1
avg_error=error/count_items_trian

print("Обучающая выборка ",count_items_trian)
print("Ср. ошибка: ",avg_error)


index=0
error=0.0
while index<count_items_test:
    y1=Y_test[index]
    y2=predicted_test[index]  
    error=error+abs(y1-y2)
    index=index+1
avg_error=error/count_items_test

print("Тестовая выборка ",count_items_test)
print("Ср. ошибка: ",avg_error)

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

Вот пример ее вывода:

Обучающая выборка 984

Ср. ошибка: 0.02032520325203252

Тестовая выборка 993

Ср. ошибка: 17.300100704934543

Как видим, на обучающей выборке модель сработала идеально, среднее отклонение почти нуль. На тестовой есть небольшая ошибка. Значение абсолютное, в данном примере оно равно 17 (у вас может получиться другое число, так как данные рандомые, да и сам алгоритм машинного обучения тоже завязан на рандоме, обычно получается от 15 до 20). Много это или мало? Давайте посмотрим.

Вот у нас цвет с красной составляющей 100, остальные по нулям:

Вот 117 (+17)

А вот 83(-17)

Как видим, разница едва заметна.

Но это среднее значения. Где-то может быть отклонение больше 17, а где-то меньше. Более точную картинку даст среднее в купе со среднеквадратичным отклонением, но мы сейчас не будет его считать, если интересно – пусть это будет вашим домашним заданием. А мы сделаем вот что: попробуем загнать на обученные модели всю картинку и посмотрим, какая картинка получиться на выходе.

Для этого воспользуемся следующей программой на Python:

import random
import pandas as pd
import numpy as np
import datetime
from sklearn.ensemble import RandomForestClassifier
from PIL import Image, ImageDraw #Подключим библиотеки для работы с картинками

def CreateModel(selection, out_name):
    ls=["RY", "GY", "BY"]
    ls.remove(out_name)
    Y = selection[out_name]
    X = selection.drop(ls, axis=1)
    model = RandomForestClassifier()
    model.fit(X, Y)
    return model

def GetColor(selection, out_name,model):
    ls=["RY", "GY", "BY"]
    ls.remove(out_name)
    Y = selection[out_name]
    X = selection.drop(ls, axis=1)
    predicted = model.predict(X)
    return predicted

image = Image.open("d:\\1\\DSCN1128.jpg") #Открываем изображение. 
draw = ImageDraw.Draw(image) #Создаем инструмент для рисования. 
width = image.size[0] #Определяем ширину. 
height = image.size[1] #Определяем высоту. 	
pix = image.load() #Выгружаем значения пикселей.
count=width*height
ls_train=[]
W=2 # Полуразмер окна
for i in range(W,width-W):
    for j in range(W,height-W):        
        if random.randint(1,int(count/2000))==1:
            dic={}
            k=1            
            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
            dic["RY"]=pix[i, j][0]
            dic["GY"]=pix[i, j][1]
            dic["BY"]=pix[i, j][2]
            ls_train.append(dic)
            
data_train=pd.DataFrame(ls_train)

modelR=CreateModel(data_train, "RY")
modelG=CreateModel(data_train, "GY")
modelB=CreateModel(data_train, "BY") 
    
#Теперь соберем каринку, сначала подготовим выборку
print(datetime.datetime.now())
new_image = Image.new("RGB", (width,height), (0,0,0))
draw = ImageDraw.Draw(new_image)  # Создаем инструмент для рисования
count=1
limit=999999999
ls_img=[]
for i in range(W,width-W):
    if count>limit:
        break;
    for j in range(W,height-W):
        if count>limit:
            break;
        count=count+1
        k=1
        dic={}
        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 
        dic["RY"]=pix[i, j][0]
        dic["GY"]=pix[i, j][1]
        dic["BY"]=pix[i, j][2]                
        ls_img.append(dic)
 
print(datetime.datetime.now())
data_img=pd.DataFrame(ls_img)

#получим предсказания и загоним их в картинку
r = GetColor(data_img,"RY",modelR) #узнаем значение красного цвета пикселя
g = GetColor(data_img,"GY",modelG) #зелёного
b = GetColor(data_img,"BY",modelB) #синего  
count=1
index=0
for i in range(W,width-W):
    if count>limit:
        break;
    for j in range(W,height-W):
        if count>limit:
            break;
        count=count+1    
        draw.point((i, j), (r[index], g[index], b[index])) #рисуем пиксель
        index=index+1

new_image.save("d:\\1\\result1.jpg", "JPEG") #не забываем сохранить изображение
print("Преобразование картинки успешно выполнено")
print(datetime.datetime.now())

Я настоятельно рекомендую сначала загнать все точки в выборку, и прогнать эту выборку целиком через модели, а потом уже по результатам зажигать точки (как сделано в данном примере). Не предъявлять модели каждую точку по отдельности, это будет работать гораздо медленнее.

Теперь посмотрим на результат:

Как видим, картинка в целом сохранилась, хотя и в искаженном виде. Почему она в черной рамке? Потому что мы использовали скользящее окно, и те точки (крайние), которые в него не попали, оказались черные. Как это исправить? Нужно дополнить исходную картинку рамкой, и обрабатывать уже дополненную картинку (шире и выше на эту рамку). Самый простой, но не очень хороший способ – просто тупо добавить по краям черные точки. Более лучший способ в качестве рамки добавить зеркальное отражение картинки. Если хотите потренироваться – пусть это будет вашим домашним заданием.

Как это можно использовать? Например, у нас есть испорченная фотография (с точками, царапинами и прочими артефактами). Используя алгоритм машинного обучения, мы можем попробовать исправить ее. Как? Берем похожую картинку. Обучаем на ней модель (таким же образом, как мы делали это сейчас). А потом дефектные пиксели заменим на нормальные. Нормальные пиксели нам подскажет модель. Для поиска дефектных пикселей мы сделаем другую модель.

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

Продолжение.

Comments

So empty here ... leave a comment!

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

Sidebar