003.00 監督式學習
建檔日期: 2019/09/17
更新日期: None
語言: Python 3.7.4, urllib3 1.25.3, numpy 1.16.4, matplotlib 3.1.0, Pillow 6.1.0
系統: Win10 Ver. 10.0.17763
主題: 003.00 監督式學習
前言:
看到人工智慧, 機器學習, 再到深度學習, 一大堆雜七雜八的資料, 得花多久的時間才能搞懂作什麼, 先找了一個範例, 這個範例是採用100張的明星照片, 建立一個模型用來分辨照片中的明星是男是女, 先區分為80張的訓練組及20張的測試組, 除了圖片以外, 還要自行確認每張照片中的明星是男是女, 建立性別資料. 該模型輸出只有兩種類別, 男或女, 或者說是不是男的, 或者說是不是女的, 這是一種二選一分類的模型, 不同的功能會有不同的模型, 這裡採用的是邏輯迴歸的演算法.
本範例主要的是採用numpy來作所有的處理, 而不是如其的人工智慧庫給你一個函式, 什麼都作完了, 這樣你不會清楚知道發生什麼事. 在本範例中瞭解了整個模型建構及演算法細節, 感覺在人工智慧上跨了一大步.
範例來源: https://datascienceintuition.wordpress.com...
要一步一步的說明細節, 不如看下圖比較直接, 後面再稍作解釋, 就容易看懂了.
1. 準備資料作為輸入
a. 照片來源
照片當然可以自己拍, 不過挺花時間的, 在學習階段, 直接上網下載就行, 網上有很多資料庫免費提供, 比如: https://lionbridge.ai/datasets/the-50-best....
一般的庫大都是一行就下載下來, 這裡是從網頁中讀取100張的照片, 存放到子目錄中, 下次就不用再下載一次
def load_100_pictures():
if not os.path.exists('img_align_celeba'):
os.mkdir('img_align_celeba')
for img_i in range(1, 101):
f = '000%03d.jpg' % img_i
url = 'https://s3.amazonaws.com/cadl/celeb-align/' + f
print(url, end='\r')
urllib.request.urlretrieve(url, os.path.join('img_align_celeba', f))
print('Celeb Net dataset already downloaded')
b. 資料分為兩群, 訓練群及測試群.
一般測試群大約佔1/4資料, 這個比例可以自行分配. 訓練群用來讓模型找到自己內部最佳的引數, 也就是wi及b. 測試群用來測試最後模型的準確性.
c. 照片為jpg檔
每一個畫素都含有三個0~255的R,G,B值, 光的三要素R(紅),G(綠),B(藍)合成在一起, 才是該畫素的顏色.
d. 照片資料的shape為(100張, 218高, 178寛, 3色)
在輸入到模型中, 必須先處理一下, 才能符合模型的輸入要求. 除了簡單化, 也可以減少記憶體的使用和處理的時間. 不過, 過度的簡化可能會造成資訊量的流失, 造成模型建造失敗.
e. 每張照片高218畫素, 寛178畫素
我們先裁成(178, 178)的正方形. 照片群就變成(100張, 178高, 178寛, 3色), 資料量一下就少了20%左右.
def imcrop_tosquare(img):
if img.shape[0] > img.shape[1]:
extra = (img.shape[0] - img.shape[1]) // 2
crop = img[extra:-extra, :]
elif img.shape[1] > img.shape[0]:
extra = (img.shape[1] - img.shape[0]) // 2
crop = img[:, extra:-extra]
else:
crop = img
return crop
f. 將照片的尺寸由(178, 178)改為(64, 64)
縮小的照小在人眼看來, 仍然可以輕易辨認出性別來. 照片群就變成(100張, 64高, 64寛, 3色), 因為這個尺寸是以平方來計算的, 所以資料量一下就少了87%左右, 總計資料量只有約原來的10%. 當然, 我們還可以再把RGB三色轉換為灰階, 資料量就會變成只有約原來的3.3%, 不過我們還是保留沒有這樣作, 當然各位可以試試.
imgs = []
for file_i in files:
# 照片格式都是(218, 178, 3), 高218, 寛178, RGB三色/每點
img = plt.imread(file_i)
# 裁成(178,178,3), 正方形照片
square = imcrop_tosquare(img)
# 以LANCZOS方法來重設大小為(64,64,3),
# 足以辨識就可以了, 照片太大, 處理時間與記憶體都會佔用太多
rsz = np.array(Image.fromarray(square).resize((64, 64), resample=Image.LANCZOS))
# list結果為(100, 64, 64, 3), 100筆, 64x64, RGB三色的照片資料
imgs.append(rsz)
g. 再把資料從 0 ~ 255 轉為 0 ~ 1
在邏輯迴歸計算時, 有使用到指數的運算, 大於1的數, 很容易就會造成系統數值溢位錯誤.
data = np.array(imgs)/255
h. 建立資料標籤
照片資料中並沒有我們所要的性別資料, 所以我們要自己看照片判斷建立. 還有建立兩個分類的標籤['Male 男', 'Female 女'], 這裡我們定義0為男性, 1為女性.
classes=np.array(['Male', 'Female'])
y=np.array([1,1,0,1,1,1,0,0,1,1,1,0,0,1,0,0,1,1,1,0,0,1,0,1,0,1,1,1,1,0,1,0,0,1,1,0,0,0,1,1,0,1,1,1,1,1,1,0,0,0,0,0,0,1,0,0,1,1,1,0,0,1,1,0,0,1,0,0,0,0,1,0,1,1,1,0,1,1,0,0,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1])
y=y.reshape(1,y.shape[0])
i. 把照片分為兩群, 訓練群及測試群
train_x_orig=data[:80,:,:,:]
test_x_orig=data[80:,:,:,:]
y_train=y[:,:80]
y_test=y[:,80:]
j. 最後我們要把二維的畫素資料轉換為一維的畫素資料
這樣才能符合模型的輸入要求. 轉換的方式是把每一行的點連續接在一起再轉置, 如此一來, 訓練組照片從(80, 64, 64, 3)變為(12288,80), 測試組照片從(20,64,64,3)變為(12288,20).
# 訓練資料80筆, 測試資料20筆, 照片每邊64點
m_train = train_x_orig.shape[0]
m_test = y_test.shape[1]
num_px = train_x_orig.shape[1]
# (80, 64, 64, 3) -> (1228,80), (20,64,64,3) -> (1228,20)
train_x = train_x_orig.reshape(train_x_orig.shape[0],-1).T
test_x = test_x_orig.reshape(test_x_orig.shape[0],-1).T
2. 對每一筆輸入訓練用資料的特徵值X或xi隨意設定權重W或wi, 並隨意設定一個偏差值b
在這裡, 簡單為主, 我們都設為0
w = np.zeros((1,12288))
b = 0
3. 計算sigma, Σ值
只要在計算前把矩陣的shape, 也就是資料擺對方向及位置, 就會很容易計算, 這也是numpy陣列強大的所在, 簡單的一個運算就把事情作完了.
sigma =np.dot(w,X)+b
4. 使用sigmoid函式
將Σ作非線性轉換成-1 ~ 1的數值, 得到我們的預測值A或a, A中的每一項a就代表該照片經模型預測的性別結果, 因為在模型中, 利用預測A與實際Y的差異去調整權重wi及偏差b, 以得到更接近實際Y的預測A, 這也是負回饋的一種應用.
def sigmoid(z):
# 建立sigmoid函式, 將輸入值非線性轉換為-1~+1的數值
s = 1/(1+np.exp(-z))
return s
A = sigmoid(np.dot(w,X)+b)
5. 以邏輯迴歸的方式, 計算誤差值
這裡稱為成本cost, 原則上, cost越小越好, 因為cost可以代表所有預測值與實際值的差異或誤差值總和.
cost = (-1/m)*(np.dot(Y,np.log(A.T))+ np.dot((1-Y),np.log((1-A).T)))
6. 根據輸入的特徵值, 預測值及測試用的實際值, 計算成本對權重wi的導數dw (實際上是dcost/dw), 以及對偏差值b的導數db (實際上是dcost/db)
在sigma Σ中, xi不是一個變數, 是已知的輸入資料, wi及b才是在模型作訓練時的變數, 所以我們要對wi及b來求導數, 用來作為迴歸方法中的反饋引數. 導數代表的意義是指該變數的變化量, 所造成輸出的變化量, 導數值越大, 變化量越大, 我們要調整wi或b的量就越大, 越接近最低點, 導數值就越小, 一直到導數值為0, 或足夠接近0, 最低點的導數或斜率就是0.
dw = (1/m)*np.dot((A-Y),X.T)
db = (1/m)*np.sum((A-Y))
7. 根據成本對權重wi的導數,對偏差值b的導數, 以及alpha或α, 修改wi及b
這裡的alpha或α, 用來對調整的量作一個限制, 因為調整太多, 會找不到最低點, 調整太少會花太多的時間在調整上, 所以alpha或α被稱為學習速率. 一般來說, alpha值大都在1以下, 當然也有使用1以上的情況, 甚至可能是上千. 這個值的設定是隨情況而定的, 也可以說是試出來的.
w = w-learning_rate*dw
b = b-learning_rate*db
8. 重複N次步驟3到步驟7, 得到最小的cost, 最適當的wi及b值.
N是模型更新wi及b的次數, 多少次才是對的, 這是不一定的, 因為要看模型的複雜度, alpha值的選擇, 通常有個上限, 因為作得再多次, 結果變化也不大. 模型中所用的方法是數值分析的一種, 基本上, 作的更多次, 會更靠近目標點, 只是更靠近, 並不一定會落在目標點上. 簡單的模型, 可能很快就可以找到目標點, 但在複雜的模型中, 作得太多次可能會更糟糕, 這種情形就叫作over-fitting過度擬合或過適, 因為這裡追求的是訓練組的磨合, 結果可能測試組的結果變差了, 而我們真正要的結果是測試組的結果.
9. 使用該模型, 根據已知最適當的wi及b值, 計算出訓練組資料的預測值
與實際值作比較, 確認訓練後的正確率是否夠好, 不夠好可以調整alpha值及N值, 也有可能是該模型的建構方式無法符合要求, 必須採用不同的模型來重新訓練及測試.
10. 使用該模型, 根據已知最適當的wi及b值, 計算出測試組資料的預測值
與實際值作比較, 確認訓練後, 該模型的正確率是否夠好, 是否可以適用到一般的應用上.
結論: 本文所述的模型因為簡單, 所以在輸入層與輸出層中, 只有一層的wi及b, 所計算出來的訓練組正確率達到100%, 但測試組的正確率卻只有65%, 可以有很多的方式來改善, 比如:
- 這裡用的梯度下降法是最常用的方法, 但只能在簡單的模型中找到目標點; 在複雜的模型中可能只是找到區域性的目標點, 而不是全域性的目標點.
- 使用更多層的wi和b, 這代表更復雜的模型, 在很多人工智慧的建模都可以看到, 如下圖.
- 資料量的保留, 縮減通常會造成資訊的流失
- 改變不同的演算法, 比如模型中的sigma Σ, sigmoid, cost, 迴歸法等等
輸出結果:
運算次數與cost的對照表
測試組中預測錯誤的七張照片
原照片與經加權/偏移計算的照片比對
附件(程式)
# -*- coding: utf-8 -*-
import os
import urllib.request
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
def load_100_pictures():
if not os.path.exists('img_align_celeba'):
os.mkdir('img_align_celeba')
for img_i in range(1, 101):
f = '000%03d.jpg' % img_i
url = 'https://s3.amazonaws.com/cadl/celeb-align/' + f
print(url, end='\r')
urllib.request.urlretrieve(url, os.path.join('img_align_celeba', f))
print('Celeb Net dataset already downloaded')
# 從網站下載100張明星照片, 在到img_align_celeba目錄下, 如果目錄已存在, 就不下載了
load_100_pictures()
# 從目錄中取得所有JPG檔案的檔名(長度100的list)
files = [os.path.join('img_align_celeba', file_i) for file_i in os.listdir('img_align_celeba') if '.jpg' in file_i]
# 自行建立一個陣列, 內容為各照片中明星的性別, 0為男性, 1為女性
# shape從(100,)改為(1,100)
y=np.array([1,1,0,1,1,1,0,0,1,1,1,0,0,1,0,0,1,1,1,0,0,1,0,1,0,1,1,1,1,0,1,0,0,1,1,0,0,0,1,1,0,1,1,1,1,1,1,0,0,0,0,0,0,1,0,0,1,1,1,0,0,1,1,0,0,1,0,0,0,0,1,0,1,1,1,0,1,1,0,0,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1])
y=y.reshape(1,y.shape[0])
# 建立分類標籤內容'Male', 'Female'
classes=np.array(['Male', 'Female'])
# 目標分為兩群, 前80張為訓練用群, 後20張為測試群
y_train=y[:,:80]
y_test=y[:,80:]
# 擷取照片成最大的正方形
def imcrop_tosquare(img):
if img.shape[0] > img.shape[1]:
extra = (img.shape[0] - img.shape[1]) // 2
crop = img[extra:-extra, :]
elif img.shape[1] > img.shape[0]:
extra = (img.shape[1] - img.shape[0]) // 2
crop = img[:, extra:-extra]
else:
crop = img
return crop
# 讀入所有的照片
imgs = []
for file_i in files:
# 照片格式都是(218, 178, 3), 高218, 寛178, RGB三色/每點
img = plt.imread(file_i)
# 裁成(178,178,3), 正方形照片
square = imcrop_tosquare(img)
# 以LANCZOS方法來重設大小為(64,64,3),
# 足以辨識就可以了, 照片太大, 處理時間與記憶體都會佔用太多
rsz = np.array(Image.fromarray(square).resize((64, 64), resample=Image.LANCZOS))
# list結果為(100, 64, 64, 3), 100筆, 64x64, RGB三色的照片資料
imgs.append(rsz)
# 降低資料大小, 避免在邏輯迴歸時, 指數運算造成系統數值溢位錯誤
data = np.array(imgs)/255
# 資料分為兩群, 前80張為訓練用群, 後20張為測試群
train_x_orig=data[:80,:,:,:]
test_x_orig=data[80:,:,:,:]
# 對只有兩種結果的事件, 採用邏輯迴歸法
# 訓練資料80筆, 測試資料20筆, 照片每邊64點
m_train = train_x_orig.shape[0]
m_test = y_test.shape[1]
num_px = train_x_orig.shape[1]
# (80, 64, 64, 3) -> (12288,80), (20,64,64,3) -> (12288,20)
train_x = train_x_orig.reshape(train_x_orig.shape[0],-1).T
test_x = test_x_orig.reshape(test_x_orig.shape[0],-1).T
# Initialize parameters W and b
def initialize_with_zeros(dim):
# 建立w是一個內容都為0, 點數大小的陣列(1, 點數12288), b為0
w = np.zeros((1,dim))
b = 0
# 確認w及b符合要求
assert(w.shape == (1, dim))
assert(isinstance(b, float) or isinstance(b, int))
return w, b
def sigmoid(z):
# 建立sigmoid函式, 將輸入值非線性轉換為-1~+1的數值
s = 1/(1+np.exp(-z))
return s
def propagate(w, b, X, Y):
'''
輸入
w -- 加權陣列, 對應到一張照片所有的點的三顏色, (點數*3, 1) (12288, 1)
b -- 偏差, 純量
X -- 輸入的資料陣列, (點數*3, 訓練用照片數) (12288, 80)
Y -- 結果卷標0, 1的陣列, (1, 訓練用照片數) (1, 80)
輸出:
cost -- 負對數可能值, 是邏輯迴歸法中的成本或誤差值
dw -- dcost/dw, (點數*3, 1)
db -- dcost/db, 純量
'''
m = X.shape[1]
# 正向傳遞 (從輸入到成本 X -> cost), A預測值(1, 80), cost = -1/m*sum(y*log(a)+(1-y)*log(1-a)
A = sigmoid(np.dot(w,X)+b)
cost = (-1/m)*(np.dot(Y,np.log(A.T))+ np.dot((1-Y),np.log((1-A).T)))
# 反向傳遞 (找出梯度grads), dZ=A-Y, dw = 1/m*(dZ . XT), db = mean(dZ)
dw = (1/m)*np.dot((A-Y),X.T)
db = (1/m)*np.sum((A-Y))
assert(dw.shape == w.shape)
assert(db.dtype == float)
cost = np.squeeze(cost)
assert(cost.shape == ())
grads = {"dw": dw,
"db": db}
return grads, cost
def optimize(w, b, X, Y, num_iterations, learning_rate, print_cost = False):
"""
利用梯度下降演算法優化 w, b
輸入
w -- 加權陣列, 對應到一張照片所有的點的三顏色, (點數*3, 1) (12288, 1)
b -- 偏差, 純量
X -- 所有輸入的資料陣列, (點數*3, 訓練用照片數) (12288, 80)
Y -- 結果卷標0, 1的陣列, (1, 訓練用照片數) (1, 80)
num_iterations -- 引數優化運算次數
learning_rate -- 引數修正學習速率
print_cost -- 是否每100次運算, 列印成本cost
輸出
params -- { } w, b
grads -- {'dw':dw, 'db':db'} cost對w的導數, cost對b的導數
costs -- [ ] 所有cost記錄, 供畫圖使用
"""
costs = []
for i in range(num_iterations):
# 按輸入資料, 目標卷標, 模型中的權重及偏移值計算成本Cost及梯度
grads, cost = propagate(w, b, X, Y)
# 從grads中取回dw, db
dw = grads["dw"]
db = grads["db"]
# 更新權重及偏差
w = w-learning_rate*dw
b = b-learning_rate*db
# 每100次記錄下cost值
if i % 100 == 0:
costs.append(cost)
# 是否每100次列印cost
if print_cost and i % 100 == 0:
print ("Cost after iteration %i: %f" %(i, cost))
# 畫cost曲線
plt.rcParams['figure.figsize'] = (10.0, 10.0)
plt.plot(np.squeeze(costs))
plt.ylabel('cost')
plt.xlabel('iterations (per hundreds)')
plt.title("Learning rate =" + str(learning_rate))
plt.show()
params = {"w": w, "b": b}
grads = {"dw": dw, "db": db}
return params, grads, costs
def predict(w, b, X):
'''
按已優化的 w, b 預測輸入的照片結果
輸入
w -- 模型中已優化的權重 (12288, 1)
b -- 偏差值
X -- 輸入料陣列 (12288, 照片數)
輸出
Y_prediction -- 預測結果
'''
m = X.shape[1]
Y_prediction = np.zeros((1,m))
#w = w.reshape(X.shape[0], 1)
# 計算預測值, 並轉成0~1的機率值, 再四捨五入取整數
A = sigmoid(np.dot(w,X)+b)
Y_prediction=np.round(A)
assert(Y_prediction.shape == (1, m))
return Y_prediction
def model(X_train, Y_train, X_test, Y_test, num_iterations = 2000, learning_rate = 0.5, print_cost = False):
"""
建立邏輯迴歸模型
輸入引數
X_train -- 訓練用輸入的資料陣列, (點數*3, 訓練用照片數) (12288, 80)
Y_train -- 訓練用結果卷標0, 1的陣列, (1, 訓練用照片數) (1, 80)
X_test -- 測試用輸入的資料陣列, (點數*3, 測試用照片數) (12288, 20)
Y_test -- 測試用結果卷標0, 1的陣列, (1, 訓練用照片數) (1, 20)
num_iterations -- 引數優化運算次數
learning_rate -- 引數修正學習速率
print_cost -- 是否每100次運算, 列印成本cost
輸出:
d -- { } 有關模型的資訊
"""
# 初值選定 (1, 12288)
m_train=X_train.shape[0]
w, b = initialize_with_zeros(m_train)
# 梯度下降優化
parameters, grads, costs = optimize(w, b, X_train, Y_train, num_iterations= num_iterations, learning_rate = learning_rate, print_cost = print_cost)
# 取出 w, b
w = parameters["w"]
b = parameters["b"]
# 檢查訓練組及測試組
Y_prediction_test = predict(w, b, X_test)
Y_prediction_train = predict(w, b, X_train)
# 列印檢查正確率
print("train accuracy: {} %".format(100*(1 - np.mean(np.abs(Y_prediction_train - Y_train)) )))
print("test accuracy: {} %".format(100*(1 - np.mean(np.abs(Y_prediction_test - Y_test)) )))
d = {"costs": costs,
"Y_prediction_test": Y_prediction_test,
"Y_prediction_train" : Y_prediction_train,
"w" : w,
"b" : b,
"learning_rate" : learning_rate,
"num_iterations": num_iterations}
return d
d = model(train_x, y_train, test_x, y_test, num_iterations = 1000, learning_rate = 0.005, print_cost = True)
def print_mislabeled_images(classes, X, y, p):
"""
顯示在測試組判斷錯誤的照片
X -- 照片資料組
y -- 正確標籤組
p -- 預測標籤組
"""
a = p + y
mislabeled_indices = np.asarray(np.where(a == 1))
plt.rcParams['figure.figsize'] = (40.0, 40.0) # set default size of plots
num_images = len(mislabeled_indices[0])
for i in range(num_images):
index = mislabeled_indices[1][i]
plt.subplot(2, num_images, i + 1)
plt.imshow(X[:,index].reshape(64,64,3), interpolation='sinc')
plt.axis('off')
plt.rc('font', size=10)
plt.title("Prediction: " + classes[int(p[0,index])] + " \n Class: " + classes[y[0,index]])
plt.show()
print_mislabeled_images(classes, test_x, y_test, d["Y_prediction_test"])
# 使用sinc函式來重建經過比重後的照片
test=d["w"].T*train_x*255
test=test.T.reshape(80,64,64,3)
plt.rcParams['figure.figsize'] = (10.0, 10.0)
plt.imshow(test[0], interpolation='sinc')
# reconstructed data by others
methods = [None, 'none', 'nearest', 'bilinear', 'bicubic', 'spline16',
'spline36', 'hanning', 'hamming', 'hermite', 'kaiser', 'quadric',
'catrom', 'gaussian', 'bessel', 'mitchell', 'sinc', 'lanczos']
# Fixing random state for reproducibility
np.random.seed(19680801)
fig, axes = plt.subplots(3, 6, figsize=(24, 12),
subplot_kw={'xticks': [], 'yticks': []})
fig.subplots_adjust(hspace=0.3, wspace=0.05)
for ax, interp_method in zip(axes.flat, methods):
ax.imshow(test[0], interpolation=interp_method, cmap=None)
ax.set_title(interp_method)
plt.show()
# compare the reconstructed images vs original.
def montage(images, saveto='montage.png'):
"""Draw all images as a montage separated by 1 pixel borders.
Also saves the file to the destination specified by `saveto`.
Parameters
----------
images : numpy.ndarray
Input array to create montage of. Array should be:
batch x height x width x channels.
saveto : str
Location to save the resulting montage image.
Returns
-------
m : numpy.ndarray
Montage image.
"""
if isinstance(images, list):
images = np.array(images)
img_h = images.shape[1]
img_w = images.shape[2]
n_plots = int(np.ceil(np.sqrt(images.shape[0])))
if len(images.shape) == 4 and images.shape[3] == 3:
m = np.ones(
(images.shape[1] * n_plots + n_plots + 1,
images.shape[2] * n_plots + n_plots + 1, 3)) * 0.5
else:
m = np.ones(
(images.shape[1] * n_plots + n_plots + 1,
images.shape[2] * n_plots + n_plots + 1)) * 0.5
for i in range(n_plots):
for j in range(n_plots):
this_filter = i * n_plots + j
if this_filter < images.shape[0]:
this_img = images[this_filter]
m[1 + i + i * img_h:1 + i + (i + 1) * img_h,
1 + j + j * img_w:1 + j + (j + 1) * img_w] = this_img
#plt.imsave(arr=m, fname=saveto)
return m
compare = np.concatenate((test[52:54], data[52:54]), axis=0)
compare.shape
plt.imshow(montage(compare,saveto='montage.png'),interpolation='spline36')
plt.show()
plt.imshow(montage(compare,saveto='montage.png'),interpolation='bicubic')
plt.show()
# generate the montage with different interpolations for comparison.
methods = [None, 'none', 'nearest', 'bilinear', 'bicubic', 'spline16',
'spline36', 'hanning', 'hamming', 'hermite', 'kaiser', 'quadric',
'catrom', 'gaussian', 'bessel', 'mitchell', 'sinc', 'lanczos']
# Fixing random state for reproducibility
np.random.seed(19680801)
fig, axes = plt.subplots(3, 6, figsize=(24, 12),
subplot_kw={'xticks': [], 'yticks': []})
fig.subplots_adjust(hspace=0.3, wspace=0.05)
for ax, interp_method in zip(axes.flat, methods):
ax.imshow(montage(compare,saveto='montage.png'), interpolation=interp_method, cmap=None)
ax.set_title(interp_method)
plt.show()