神經(jīng)網(wǎng)絡(luò)學(xué)習(xí)“你拍我猜” —— 你拍照,AI猜
在這個(gè)項(xiàng)目中,你將學(xué)習(xí)利用神經(jīng)網(wǎng)絡(luò)來(lái)分類照片中是狗狗,是貓貓,還是人。
本項(xiàng)目使用了一個(gè)經(jīng)過(guò)預(yù)處理后較小的數(shù)據(jù)集,數(shù)據(jù)集中僅含有圖像的特征結(jié)果。對(duì)于如何獲取圖像的特征,這里附上了open cv中對(duì)于圖像特征的說(shuō)明。
http://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_feature2d/py_features_meaning/py_features_meaning.html
在該 notebook 中,我們基于以下三個(gè)特征來(lái)了解圖像是狗狗,貓貓還是人的概率:
- Feature1
- Feature2
- Feature3
- Feature4
‘class’是0,代表是人;1代表是貓貓;2代表是狗狗;
每一行代表一個(gè)圖像;
加載數(shù)據(jù)
為了加載數(shù)據(jù)并很好地進(jìn)行格式化,我們將使用兩個(gè)非常有用的包,即 Pandas 和 Numpy。 你可以在這里閱讀文檔:
%matplotlib inline
# Importing pandas and numpy
import pandas as pd
import numpy as np
from IPython.display import display
# present all plots in the notebook
# Reading the csv file into a pandas DataFrame
dataset = pd.read_csv('data.csv')
#random all the rows in dataset
dataset = dataset.sample(frac=1)
# print data shortcut
dataset[:10]
| feature1 | feature2 | feature3 | feature4 | class | |
|---|---|---|---|---|---|
| 59 | 5.2 | 1080.0 | 3.9 | 14.0 | 1 |
| 146 | 6.3 | 1000.0 | 5.0 | 19.0 | 2 |
| 1 | 4.9 | 1200.0 | 1.4 | 2.0 | 0 |
| 118 | 7.7 | 1040.0 | 6.9 | 23.0 | 2 |
| 124 | 6.7 | 1320.0 | 5.7 | 21.0 | 2 |
| 96 | 5.7 | 1160.0 | 4.2 | 13.0 | 1 |
| 42 | 4.4 | 1280.0 | 1.3 | 2.0 | 0 |
| 95 | 5.7 | 1200.0 | 4.2 | 12.0 | 1 |
| 145 | 6.7 | 1200.0 | 5.2 | 23.0 | 2 |
| 40 | 5.0 | 1400.0 | 1.3 | 3.0 | 0 |
數(shù)據(jù)分析 - 繪制數(shù)據(jù),可視化的數(shù)據(jù)分析
首先讓我們對(duì)數(shù)據(jù)進(jìn)行繪圖,看看他們互相之間的關(guān)系是什么。首先來(lái)看試一下feature1和feature2
# Importing matplotlib
import matplotlib.pyplot as plt
# Function to help us plot
def plot_points(dataset):
X = np.array(dataset[["feature1","feature2"]])
y = np.array(dataset["class"])
people = X[np.argwhere(y==0)]
cat = X[np.argwhere(y==1)]
dog = X[np.argwhere(y==2)]
plt.scatter([s[0][0] for s in people], [s[0][1] for s in people], s = 25, color = 'red', edgecolor = 'k')
plt.scatter([s[0][0] for s in cat], [s[0][1] for s in cat], s = 25, color = 'cyan', edgecolor = 'k')
plt.scatter([s[0][0] for s in dog], [s[0][1] for s in dog], s = 25, color = 'yellow', edgecolor = 'k')
plt.xlabel('Feature_1')
plt.ylabel('Feature_2')
# Plotting the points
plot_points(dataset)
plt.show()

圖上紅色是人,青色是小貓,黃色是小狗。 粗略來(lái)說(shuō),這兩個(gè)feature并沒(méi)有很好地分離圖像小狗,小貓和人。 也許將另兩個(gè)features考慮進(jìn)來(lái)會(huì)有幫助? 接下來(lái)我們將繪制一組圖,用seaborn的pairplot函數(shù)來(lái)試試吧!
https://seaborn.pydata.org/generated/seaborn.pairplot.html
# plotting high-dimensional
import seaborn as sns
sns.pairplot(dataset, hue='class', vars=["feature1","feature2","feature3","feature4"])

圖上class=0,代表是人;1代表是貓貓;2代表是狗狗;
任務(wù)1: 將訓(xùn)練集拆分成自變量data及應(yīng)變量標(biāo)簽label的組合
數(shù)據(jù)集中['feature1','feature2','feature3','feature4']是自變量data;
['class']則是應(yīng)變量標(biāo)簽label;
可參考使用pandas中的iloc,loc用法。
https://pandas.pydata.org/pandas-docs/version/0.21/generated/pandas.DataFrame.iloc.html
https://pandas.pydata.org/pandas-docs/version/0.22/generated/pandas.DataFrame.loc.html
# separate dataset into data - feature table and label table
data = dataset.iloc[:,:4]
label = dataset.loc[:,'class']
display(data[:10])
display(label[:10])
| feature1 | feature2 | feature3 | feature4 | |
|---|---|---|---|---|
| 59 | 5.2 | 1080.0 | 3.9 | 14.0 |
| 146 | 6.3 | 1000.0 | 5.0 | 19.0 |
| 1 | 4.9 | 1200.0 | 1.4 | 2.0 |
| 118 | 7.7 | 1040.0 | 6.9 | 23.0 |
| 124 | 6.7 | 1320.0 | 5.7 | 21.0 |
| 96 | 5.7 | 1160.0 | 4.2 | 13.0 |
| 42 | 4.4 | 1280.0 | 1.3 | 2.0 |
| 95 | 5.7 | 1200.0 | 4.2 | 12.0 |
| 145 | 6.7 | 1200.0 | 5.2 | 23.0 |
| 40 | 5.0 | 1400.0 | 1.3 | 3.0 |
59 1
146 2
1 0
118 2
124 2
96 1
42 0
95 1
145 2
40 0
Name: class, dtype: int64
任務(wù)2: 將分類進(jìn)行 One-hot 編碼
為了實(shí)現(xiàn)softmax的概率分布,我們將使用Pandas 中的 get_dummies 函數(shù)來(lái)對(duì)label進(jìn)行One-hot編碼。
問(wèn)題1: one-hot編碼的作用是什么呢?
回答:計(jì)算機(jī)只能讀懂?dāng)?shù)字,而不能理解具體的分類類型,onehot編碼就是對(duì)于那些離散的特征轉(zhuǎn)換成數(shù)字編碼后,依然能保留它們相互之間離散的特性,比如鴨子 海貍 海象 如果編碼編成0 1 2,雖然這三個(gè)動(dòng)物沒(méi)有任何關(guān)系,但是0 1 2卻有數(shù)學(xué)上的大小和順序,這個(gè)關(guān)系顯然在原特征中是不存在的,而onehot編碼后,三個(gè)編碼從一維數(shù)軸上的0 1 2變成了三維相互正交的坐標(biāo)軸上的100 010 001,彼此無(wú)任何順序和大小關(guān)系。
# TODO: Make dummy variables for labels
dummy_label = pd.get_dummies(label)
# Print the first 10 rows of our data
dummy_label[:10]
| 0 | 1 | 2 | |
|---|---|---|---|
| 59 | 0 | 1 | 0 |
| 146 | 0 | 0 | 1 |
| 1 | 1 | 0 | 0 |
| 118 | 0 | 0 | 1 |
| 124 | 0 | 0 | 1 |
| 96 | 0 | 1 | 0 |
| 42 | 1 | 0 | 0 |
| 95 | 0 | 1 | 0 |
| 145 | 0 | 0 | 1 |
| 40 | 1 | 0 | 0 |
任務(wù)3: 數(shù)據(jù)標(biāo)準(zhǔn)化
由于神經(jīng)網(wǎng)絡(luò)是計(jì)算權(quán)重,因此我們需要對(duì)數(shù)據(jù)進(jìn)行標(biāo)準(zhǔn)化的預(yù)處理。 我們注意到feature2和feature4的范圍比f(wàn)eature1和feature3要大很多,這意味著我們的數(shù)據(jù)存在偏差,使得神經(jīng)網(wǎng)絡(luò)很難處理。 讓我們將兩個(gè)特征縮小,使用(x-min)/(max-min))來(lái)將特征歸到(0, 1)。
# TODO: Scale the columns
data['feature2'] = (data['feature2']-data['feature2'].min())/(data['feature2'].max()-data['feature2'].min())
data['feature4'] = (data['feature4']-data['feature4'].min())/(data['feature4'].max()-data['feature4'].min())
# Printing the first 10 rows of our procesed data
data[:10]
| feature1 | feature2 | feature3 | feature4 | |
|---|---|---|---|---|
| 59 | 5.2 | 0.291667 | 3.9 | 0.541667 |
| 146 | 6.3 | 0.208333 | 5.0 | 0.750000 |
| 1 | 4.9 | 0.416667 | 1.4 | 0.041667 |
| 118 | 7.7 | 0.250000 | 6.9 | 0.916667 |
| 124 | 6.7 | 0.541667 | 5.7 | 0.833333 |
| 96 | 5.7 | 0.375000 | 4.2 | 0.500000 |
| 42 | 4.4 | 0.500000 | 1.3 | 0.041667 |
| 95 | 5.7 | 0.416667 | 4.2 | 0.458333 |
| 145 | 6.7 | 0.416667 | 5.2 | 0.916667 |
| 40 | 5.0 | 0.625000 | 1.3 | 0.083333 |
任務(wù)4: 將數(shù)據(jù)分成訓(xùn)練集和測(cè)試集
為了測(cè)試我們的算法,我們將數(shù)據(jù)分為訓(xùn)練集和測(cè)試集。 測(cè)試集的大小將占總數(shù)據(jù)的 10%。
你可以使用numpy.random.choice或者sklearn.model_selection.train_test_split函數(shù)。
https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.choice.html
http://scikit-learn.org/stable/modules/generated/sklearn.model_selection.train_test_split.html
問(wèn)題2: 拆分測(cè)試集的目的是什么?還有其他的拆分方式嗎?
你的回答:訓(xùn)練集用來(lái)訓(xùn)練模型,數(shù)據(jù)集用來(lái)測(cè)試訓(xùn)練模型的準(zhǔn)確性,測(cè)試集必須與訓(xùn)練模型無(wú)相關(guān)性,否則就不能客觀評(píng)價(jià)模型的準(zhǔn)確率。其他方法比如還可以繼續(xù)將訓(xùn)練集再分成k份,每次留出一個(gè)子集作為驗(yàn)證集,剩余k-1個(gè)子集作為訓(xùn)練集,重復(fù)訓(xùn)練k次,平均k次結(jié)果,即k折交叉驗(yàn)證。
# TODO: split train and test dataset
from sklearn.model_selection import train_test_split
train_data, test_data, train_label, test_label= train_test_split(data, dummy_label, test_size=0.10, random_state=22)
print("Number of training samples is", len(train_data))
print("Number of testing samples is", len(test_data))
print(train_data[:10])
print(test_data[:10])
print(train_label[:10])
print(test_label[:10])
Number of training samples is 135
Number of testing samples is 15
feature1 feature2 feature3 feature4
116 6.5 0.416667 5.5 0.708333
81 5.5 0.166667 3.7 0.375000
6 4.6 0.583333 1.4 0.083333
38 4.4 0.416667 1.3 0.041667
39 5.1 0.583333 1.5 0.041667
20 5.4 0.583333 1.7 0.041667
18 5.7 0.750000 1.7 0.083333
113 5.7 0.208333 5.0 0.791667
26 5.0 0.583333 1.6 0.125000
140 6.7 0.458333 5.6 0.958333
feature1 feature2 feature3 feature4
25 5.0 0.416667 1.6 0.041667
57 4.9 0.166667 3.3 0.375000
87 6.3 0.125000 4.4 0.500000
61 5.9 0.416667 4.2 0.583333
0 5.1 0.625000 1.4 0.041667
141 6.9 0.458333 5.1 0.916667
133 6.3 0.333333 5.1 0.583333
75 6.6 0.416667 4.4 0.541667
71 6.1 0.333333 4.0 0.500000
14 5.8 0.833333 1.2 0.041667
0 1 2
116 0 0 1
81 0 1 0
6 1 0 0
38 1 0 0
39 1 0 0
20 1 0 0
18 1 0 0
113 0 0 1
26 1 0 0
140 0 0 1
0 1 2
25 1 0 0
57 0 1 0
87 0 1 0
61 0 1 0
0 1 0 0
141 0 0 1
133 0 0 1
75 0 1 0
71 0 1 0
14 1 0 0
任務(wù)5: 訓(xùn)練多分類的神經(jīng)網(wǎng)絡(luò)
下列函數(shù)會(huì)訓(xùn)練二層神經(jīng)網(wǎng)絡(luò)。 首先,我們將寫一些 helper 函數(shù)。
- Softmax 激活函數(shù)
p指代x的特征數(shù)量;
softmax函數(shù)常用于多分類目標(biāo)的模型,他會(huì)把所有的output對(duì)sum(output)進(jìn)行均一化,用于減少模型預(yù)測(cè)偏差。https://zh.wikipedia.org/wiki/Softmax%E5%87%BD%E6%95%B0
sigmoid函數(shù)常用于二分類目標(biāo)的模型,他會(huì)將離散數(shù)值轉(zhuǎn)換為概率數(shù)值。https://zh.wikipedia.org/wiki/S%E5%87%BD%E6%95%B0
- 誤差函數(shù) :交叉熵
m 為 分類的類別數(shù)。
# TODO: Activation (softmax) function
def softmax(x):
exp_x = np.exp(x)
return exp_x / np.sum(exp_x)
def loss_CE(x,y,y_hat):
return -np.sum(y* np.log(y_hat))
反向誤差傳遞函數(shù)
現(xiàn)在輪到你來(lái)練習(xí),編寫誤差項(xiàng)。 記住這是由方程
給出的。
建議:此處可以使用numpy.reshape()或者numpy.newaxis()來(lái)實(shí)現(xiàn);
# TODO: Write the error term formula
def error_term_formula(x, y, y_hat):
return -np.dot(x.reshape(-1,1), (y-y_hat).reshape(1,-1))
# Training function
def train_nn(features, targets, epochs, learnrate):
# Use to same seed to make debugging easier
np.random.seed(42)
n_records, n_features = features.shape
last_loss = None
# Initialize weights
weights = np.zeros([features.shape[1],targets.shape[1]])
for e in range(epochs):
del_w = np.zeros(weights.shape)
loss = []
for x, y in zip(features.values, targets.values):
# Loop through all records, x is the input, y is the target
# Activation of the output unit
# Notice we multiply the inputs and the weights here
# rather than storing h as a separate variable
output = softmax(np.dot(x, weights))
# The error, the target minus the network output
error = loss_CE(x, y, output)
loss.append(error)
# The error term
error_term = error_term_formula(x, y, output)
#print(weights.shape)
del_w += error_term
# Update the weights here. The learning rate times the
# change in weights, divided by the number of records to average
weights -= learnrate * del_w / n_records
# Printing out the mean error on the training set
if e % (epochs / 10) == 0:
#out = softmax(np.dot(x, weights))
loss = np.mean(np.array(loss))
print("Epoch:", e)
if last_loss and last_loss < loss:
print("Train loss: ", loss, " WARNING - Loss Increasing")
else:
print("Train loss: ", loss)
last_loss = loss
loss = []
print("=========")
print("Finished training!")
return weights
任務(wù)6: 訓(xùn)練你的神經(jīng)網(wǎng)絡(luò)
設(shè)置你的超參數(shù),訓(xùn)練你的神經(jīng)網(wǎng)絡(luò)
問(wèn)題3: learnrate的設(shè)置有什么技巧?
回答:從一個(gè)較小值開(kāi)始一點(diǎn)點(diǎn)試,當(dāng)發(fā)生loss increase時(shí),說(shuō)明步長(zhǎng)過(guò)大了,無(wú)法收斂,找到一個(gè)既能收斂有不至于收斂太慢的值
# TODO: SET Neural Network hyperparameters
epochs = 1000
learnrate = 0.18
weights = train_nn(train_data, train_label, epochs, learnrate)
Epoch: 0
Train loss: 1.09861228867
=========
Epoch: 100
Train loss: 0.550774008836
=========
Epoch: 200
Train loss: 0.446943578961
=========
Epoch: 300
Train loss: 0.281037086602
=========
Epoch: 400
Train loss: 0.193038619336
=========
Epoch: 500
Train loss: 0.182039814143
=========
Epoch: 600
Train loss: 0.174945169191
=========
Epoch: 700
Train loss: 0.169294951329
=========
Epoch: 800
Train loss: 0.164635233793
=========
Epoch: 900
Train loss: 0.160701520788
=========
Finished training!
任務(wù)7:計(jì)算測(cè)試 (Test) 數(shù)據(jù)的精確度
現(xiàn)在你的結(jié)果是One-Hot編號(hào)后的,想想如何獲取的精度上的比較?
# TODO: Calculate accuracy on test data
tes_out = softmax(np.dot(test_data, weights))
predictions = pd.get_dummies(np.argmax((tes_out),axis=1))
accuracy = np.equal(test_label,predictions).mean().min()
print("Prediction accuracy: {:.3f}".format(accuracy))
Prediction accuracy: 0.933
任務(wù)8:用你的神經(jīng)網(wǎng)絡(luò)來(lái)預(yù)測(cè)圖像是什么
在“images/”路徑下有兩張圖片,我們已經(jīng)使用通過(guò)圖像提取特征的方式,分別得到了他們的4個(gè)feature值,存儲(chǔ)在“validations.csv”中。
下面就由你來(lái)試試,看看你的神經(jīng)網(wǎng)絡(luò)能不能準(zhǔn)確的預(yù)測(cè)他們吧!
# TODO: Open the 'validations.csv' file and predict the label.
# Remember, 0 = people, 1 = cat, 2 = dog
valid=pd.read_csv('./images/validations.csv')
valid['feature2'] = (valid['feature2']-dataset['feature2'].min())/(dataset['feature2'].max()-dataset['feature2'].min())
valid['feature4'] = (valid['feature4']-dataset['feature4'].min())/(dataset['feature4'].max()-dataset['feature4'].min())
print(valid)
valid_out= softmax(np.dot(valid, weights))
valid_predictions = pd.get_dummies(np.argmax((valid_out),axis=1))
print (valid_out)
print (valid_predictions)
feature1 feature2 feature3 feature4
0 6.2 0.583333 5.4 0.916667
1 5.9 0.416667 5.1 0.708333
[[ 3.65878501e-06 1.73262510e-02 6.54050560e-01]
[ 6.00144459e-06 2.25746156e-02 3.06038913e-01]]
| 2 | |
|---|---|
| 0 | 1 |
| 1 | 1 |
第一個(gè)是2(狗) 第二個(gè)是2(狗)
任務(wù)9:(選做)神經(jīng)網(wǎng)絡(luò)分類算法的拓展應(yīng)用
經(jīng)過(guò)上面的神經(jīng)網(wǎng)絡(luò)訓(xùn)練,我們已經(jīng)得到一個(gè)可以猜對(duì)三個(gè)對(duì)象的網(wǎng)絡(luò)了!
如果想讓你的神經(jīng)網(wǎng)絡(luò)判斷更多的對(duì)象,我們就需要提供更多有標(biāo)簽的數(shù)據(jù)供他學(xué)習(xí)。
同時(shí),我們也要教會(huì)我們的神經(jīng)網(wǎng)絡(luò)什么是特征(這個(gè)部分,我們已經(jīng)幫你做好了:))。當(dāng)我們把神經(jīng)網(wǎng)絡(luò)變得更深的時(shí)候,多層的神經(jīng)網(wǎng)絡(luò)就可以用來(lái)提取圖像中的特征了!在正式的課程中,我們就會(huì)接觸到深層網(wǎng)絡(luò)的實(shí)現(xiàn)。
在這里,我們先借一個(gè)已經(jīng)訓(xùn)練好能夠識(shí)別1000個(gè)物體的網(wǎng)絡(luò)來(lái)完成“你拍,我猜”的神奇功能吧。你可以隨便上傳一張照片到“images”的文件夾下,我們的神經(jīng)網(wǎng)絡(luò)就可以根據(jù)已經(jīng)學(xué)習(xí)好的權(quán)重來(lái)猜你拍的照片是什么哦!快來(lái)試試吧!
上傳的方法點(diǎn)擊左上方的Jupyter圖標(biāo),回到上級(jí)目錄,進(jìn)入‘/images’文件夾,并upload你所要分類的圖片;
from ResNet_CAM import *
import glob
lists = glob.glob('images/*.jpg')
# TODO: Upload your image or pick up any image in the folder 'images/xx.png'
for img_path in lists:
fig, (ax1, ax2) = plt.subplots(1,2)
CAM = plot_CAM(img_path,ax1,ax2,fig)
plt.show()


傳了一張柯基,一張貴賓犬,都識(shí)別了,好神奇!