1. 程式人生 > >11.用深度學習方法為影象中的物體進行分類

11.用深度學習方法為影象中的物體進行分類

這幾個庫現在更新了,用書上的會出錯,未解決,建議直接學新的

# -*- coding: utf-8 -*-
"""
Created on Sun Oct 14 09:09:58 2018

@author: asus
"""
#11 用深度學習方法為影象中的物體進行分類
import os
batch1_filename = os.path.join(
        "E:\\books\Python資料探勘入門與實踐\用深度學習方法為影象中的物體進行分類",
        "cifar-10-batches-py", "data_batch_1")

def unpickle(file):
    import pickle
    with open(file, 'rb') as fo:
        dict = pickle.load(fo, encoding='bytes')
    return dict

batch1 = unpickle(batch1_filename)
#這一批影象檔案讀進來後為一個字典結構,包含numpy陣列形式的影象資料、影象類別、檔名
#以及該批檔案的簡短說明(training batch 1 of 5 表示訓練集共五批,這是第一批)。
#提取一張影象
image_index = 100
image = batch1[b'data'][image_index]
#影象資料格式與matplotlib(繪製圖像)所使用的有所不同,因此,要改變陣列形狀,對矩陣進行
#轉換。
image = image.reshape((32, 32, 3), order='F')
import numpy as np
image = np.rot90(image, -1)
#之後,就可以用matplotlib繪製圖像
%matplotlib inline
from matplotlib import pyplot as plt
plt.imshow(image)

#11.3 深度神經網路
#深度神經網路和基本神經網路的差別在於規模大小。至少包含兩層隱含層的神經網路被稱為深度
#神經網路。實際工作者遇到的深度神經網路通常規模很大,每層神經元數量和層次都非常多。

#神經網路接收很基礎的特徵作為輸出——就計算機視覺而言,輸入為簡單的畫素值。神經網路演算法
#把這些資料整合起來向網路中傳輸,在這個過程中,基本的特徵組合為複雜的特徵。計算機依靠
#它們進行分類。

#11.3.3 Theano簡介
#Theano使用來建立和執行數學表示式的工具。在Theano中,我們定義函式要做什麼而不是怎麼做
#這樣才能以最佳的方式對錶達式進行求值。
#我們可以用Theano來定義函式,處理標量、陣列和矩陣以及其他數學表示式。如我們可以建立計
#算直角三角形斜邊長度的函式。
import theano
from theano import tensor as T
a = T.dscalar() #標量
b = T.dscalar()
c = T.sqrt(a ** 2 + b ** 2) #c是一個表示式,即不是函式,也不是數值

f = theano.function([a,b], c) #函式 f(3,4)
#這個函式接收a、b,返回經過計算得到的結果,也就是輸出值c

#11.3.4 Lasagne簡介
#該庫是專門用來構建神經網路的,實踐了幾種比較新的神經網路層和組成這些層的模組
#內建網路層(Network-in-network layers):這些小神經網路比傳統的神經網路層更容易解釋。
#刪除層(Dropout layers):訓練過程隨機刪除神經元,放置產生神經網路常見的過擬合問題。
#噪音層(Noise layers):為神經元引入噪音,也是為了解決過擬合問題。

#本章使用卷積層(convolution layers, 層級結構模擬人類視覺工作原理)。卷積層使用少量的
#相互連線的神經元,分析一部分輸入值,便於神經網路實現對資料的標準轉換,比如對影象資料
#的轉換。視覺分析實驗,就是用卷積層對影象進行轉換。

#用第一章的Iris資料集測試卷積神經網路
from sklearn.datasets import load_iris
iris = load_iris()
X = iris.data.astype(np.float32)
y_true = iris.target.astype(np.int32)
#Lasagne對資料型別有特殊要求,因此,需要把類別值轉換為int32型別(在原始資料集中用int64
#型別儲存)。
#把資料集分為訓練集和測試集兩部分
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y_true, random_state=14)

#接著,分別建立卷積神經網路各層。
#首先,建立輸入層,其神經元數量跟資料集特徵數量相同。可以指定每一批輸入的數量(設定為10)
#這樣Lasagne可以在訓練階段做些優化。
import lasagne
input_layer = lasagne.layers.InputLayer(shape=(10, X.shape[1]))
#接著建立隱含層。該層從輸入層接收輸入(由第一個引數指定),該層一共有12個神經元,使用非
#線性的sigmoid函式。
hidden_layer = lasagne.layers.DenseLayer(
        input_layer, num_units=12, 
        nonlinearity=lasagne.nonlinearities.sigmoid)
#接著建立輸出層,它接受來自隱含層的輸入,輸出層共有三個神經元,使用非線性的softmax函
#數主要用於神經網路的最後一層。
output_layer = lasagne.layers.DenseLayer(hidden_layer, num_units=3,nonlinearity
                                         =lasagne.nonlinearities.softmax)

#為了訓練剛建立的網路,我們需要定義幾個Theano訓練函式。在這之前,需要定義一個Theano表
#達式和函式。我們先來為神經網路的輸入資料、輸入結果和實際輸出結果宣告變數。
import theano.tensor as T
net_input = T.matrix('net_input')
net_output = output_layer.get_output(net_input)
true_output = T.ivector('true_output')
#接著定義損失函式,訓練函式如何提升網路效果需要參考它的返回值——訓練神經網路時,我們以
#最小化損失函式的返回值為前提。我們用類別交叉熵表示損失,這是一種衡量分類資料分類效果
#好壞的標準。損失函式表示的是網路的期望輸出和實際輸出兩者之間的差距。
loss = T.mean(T.nnet.categorical_crossentropy(net_output, true_output))
#接著定義修改網路權重的函式。我們獲取到網路的所有引數,建立調整權重的函式,使損失降低
#到最小。
all_params = lasagne.layers.get_all_params(output_layer)
updates = lasagne.updates.sgd(loss, all_params, learning_rate=0.1)
#最後,建立兩個Theano函式,先是訓練網路,然後獲取網路的輸出,以用於後續測試。
import theano
train = theano.function([net_input, true_output], loss, updates=updates)
get_output = theano.function([net_input], net_output)
#然後呼叫訓練函式,在訓練集上進行一輪迭代,接收訓練資料,預測類別,與給定類別作比較,
#更新特徵權重,以最小化損失。然後再進行1000次迭代,逐漸改進神經網路。
for n in range(1000):
    train(X_train, y_train)
#接著,對輸出計算F值,以評估分類效果。
y_output = get_output(X_test)
import numpy as np
y_pred = np.argmax(y_output, axis=1)
from sklearn.metrics import f1_score
print(f1_score(y_test, y_pred))

#11.3.5 用nolearn實現神經網路
import numpy as np
from PIL import Image, ImageDraw, ImageFont
from skimage.transform import resize
from skimage import transform as tf
from skimage.measure import label, regionprops
from sklearn.utils import check_random_state
from sklearn.preprocessing import OneHotEncoder
from sklearn.cross_validation import train_test_split

def create_captcha(text, shear=0, size=(100, 24)):
    im = Image.new("L", size, "black")
    draw = ImageDraw.Draw(im)
    font = ImageFont.truetype(r"Coval.otf", 22)
    draw.text((2, 2), text, fill=1, font=font)
    image = np.array(im)
    affine_tf = tf.AffineTransform(shear=shear)
    image = tf.warp(image, affine_tf)
    return image / image.max()


def segment_image(image):
    labeled_image = label(image > 0)
    subimages = []
    for region in regionprops(labeled_image):
        start_x, start_y, end_x, end_y = region.bbox
        subimages.append(image[start_x:end_x,start_y:end_y])
    if len(subimages) == 0:
        return [image,]
    return subimages

random_state = check_random_state(14)
letters = list("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
shear_values = np.arange(0, 0.5, 0.05)

def generate_sample(random_state=None):
    random_state = check_random_state(random_state)
    letter = random_state.choice(letters)
    shear = random_state.choice(shear_values)
    return create_captcha(letter, shear=shear, size=(20, 20)), letters.index(letter)
dataset, targets = zip(*(generate_sample(random_state) for i in range(3000)))
dataset = np.array(dataset, dtype='float')
targets =  np.array(targets)

onehot = OneHotEncoder()
y = onehot.fit_transform(targets.reshape(targets.shape[0],1))
y = y.todense().astype(np.float32)

dataset = np.array([resize(segment_image(sample)[0], (20, 20)) for sample in dataset])
X = dataset.reshape((dataset.shape[0], dataset.shape[1] * dataset.shape[2]))
X = X / X.max()
X = X.astype(np.float32)

X_train, X_test, y_train, y_test = \
    train_test_split(X, y, train_size=0.9, random_state=14)

#神經網路由一系列的層組成。在nolearn中實現神經網路,只徐定義它由Lasagne哪幾種類型的層
#組成就行,跟PyBrain做法大同小異。
from lasagne import layers
layers=[
        ('input', layers.InputLayer),
        ('hidden', layers.DenseLayer),
        ('output', layers.DenseLayer),
        ]
from lasagne import updates
from nolearn.lasagne import NeuralNet
from lasagne.nonlinearities import sigmoid, softmax
net1 = NeuralNet(layers=layers,
                 input_shape=X.shape,
                 hidden_num_units=100,
                 output_num_units=26,
                 hidden_nonlinearity=sigmoid,
                 output_nonlinearity=softmax,
                 hidden_b=np.zeros((100,), dtype=np.float64),
                 update=updates.momentum,
                 update_learning_rate=0.9,
                 update_momentum=0.1,
                 regression=True,
                 max_epochs=1000,
                 )
net1.fit(X_train, y_train)

y_pred = net1.predict(X_test)
y_pred = y_pred.argmax(axis=1)
assert len(y_pred) == len(X_test)
if len(y_test.shape) > 1:
    y_test = y_test.argmax(axis=1)
print(f1_score(y_test, y_pred))

#11.4 GPU優化

#11.6 應用
#用CIFAR影象建立資料集。保留畫素結構——畫素的行列號。
import os
import numpy as np
batches = []
for i in range(1, 6):
    batch_filename = os.path.join(
        "E:\\books\Python資料探勘入門與實踐\用深度學習方法為影象中的物體進行分類",
        "cifar-10-batches-py", "data_batch_{}".format(i))
    batches.append(unpickle(batch_filename))
    break
#把每批次的影象檔案一次新增到資料裡。
X = np.vstack([batch[b'data'] for batch in batches])
#然後,把畫素歸一化,並將其強制轉換為32位浮點型資料(GPU進行計算的虛擬機器唯一支援的資料
#型別)。
X = np.array(X) / X.max()
X = X.astype(np.float32)
#類別的處理方法相同,只不過我們使用hstack,它為陣列末尾追加一列資料。然後使用
#OneHotEncoder把它轉換為只含有一維有效編碼的陣列。
from sklearn.preprocessing import OneHotEncoder
y = np.hstack(batch[b'labels'] for batch in batches).flatten()
y = OneHotEncoder().fit_transform(y.reshape(y.shape[0],1)).todense()
y = y.astype(np.float32)
#把資料集切分為訓練集和測試集。
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
#調整陣列形狀以保留原始影象的資料結構。影象原本是32畫素見方,每個畫素由三個值組成(分別
#表示紅、綠、藍的顏色值)。

#11.6.2 建立神經網路
#建立神經網路的各層
from lasagne import layers
layers = [
        ('input', layers.InputLayer),
        ('conv1', layers.Conv2DLayer),
        ('pool1', layers.MaxPool2DLayer),
        ('conv2', layers.Conv2DLayer),
        ('pool2', layers.MaxPool2DLayer),
        ('conv3', layers.Conv2DLayer),
        ('pool3', layers.MaxPool2DLayer),
        ('hidden4', layers.DenseLayer),
        ('hidden5', layers.DenseLayer),
        ('output', layers.DenseLayer),
        ]
#最後三層使用密集層,但是前面使用三組卷積層和池化層。此外,我們必須以輸入層開始。這樣
#一共有10層。輸入資料跟資料集同型,而不只跟輸入層的神經元數量相同,輸入層和輸出層的大
#小仍由資料集來定。
#開是建立神經網路
import theano
import lasagne
from nolearn.lasagne import NeuralNet 
from lasagne.nonlinearities import sigmoid, softmax
nnet = NeuralNet(layers=layers, input_shape=(None, 3, 32, 32),
                 conv1_num_filters=32,
                 conv1_filter_size=(3, 3),
                 conv2_num_filters=64,
                 conv2_filter_size=(2, 2),
                 conv3_num_filters=128,
                 conv3_filter_size=(2, 2),
                 pool1_ds=(2,2),
                 pool2_ds=(2,2),
                 pool3_ds=(2,2),
                 hidden4_num_units=500,
                 hidden5_num_units=500,
                 output_num_units=10,
                 output_nonlinearity=softmax,
                 updata_learning_rate=0.01,
                 update_momentum=0.9,
                 regression=True,
                 max_epochs=3,
                 verbose=1)

#11.6.3 組裝起來
nnet.fit(X_train, y_train)
from sklearn.metrics import f1_score
y_pred = nnet.predict(X_test)
print(f1_score(y_test.argmax(axis=1), y_pred.argmax(axis=1)))