CS231n是斯坦福大学视觉与学习实验室开设的面向视觉识别的卷积神经网络公开课,主讲是李飞飞及其学生。该课程(最新Spring 2019)是学习深度学习与机器视觉必备的入门课程之一。本学习系列主要是记录自己三个课后作业以及对课程的学习笔记。
本篇文章主要记录Assignment #1的学习情况。Assignment #1包含5个小作业:kNN、SVM、Softmax、Two-Layer NN和Image Features Representations,课程提纲详见Schedule and Syllabus,课程笔记参考CS231n官方笔记授权翻译,课程视频见2017CS231n 斯坦福李飞飞计算机视觉识别。
一、Setup Instructions——本作业代码在阿里云Linux服务器上完成
1.从阿里云Oss上下载作业1和cifar10数据集。由于cifar10数据集和assignment1代码在阿里云服务器下载速度非常慢,这里先下载到本地然后上传到阿里云Oss上面去,以后每次计算时直接从Oss上去下载,非常方便。阿里云ECS-OSS相关操作详见命令行工具ossutil与Bucket挂载工具ossfs
./ossutil64 ls oss://deeplearning-dataset
#下载cifar10数据集和assignment1代码到阿里云服务器...
2.在阿里云服务器上安装Anaconda(注意先修改为清华conda源会比较快),并根据assignment1中requirements.txt安装虚拟环境cs231n所需的包(需修改为阿里云PyPI 源)
conda create -n cs231n python=3.7 #create an environment called cs231n
conda activate cs231n #activate and enter the environment
pip install -r requirements.txt
3.在Pycharm上配置Jupyter Notebook,参考这篇文章。这时就可以着手开始KNN.ipynb了。
二、k-Nearest Neighbor分类器
本节主要难点在于无循环计算训练集与测试集之间的距离,可以参考:cs231n assignment1 knn 部分,kNN代码见这里
若测试集矩阵为 P P P大小为 M × D M \times D M×D, 训练集矩阵为 C C C大小为 N × D N \times D N×D:
那么
P
i
P_{i}
Pi(
P
P
P的第
i
i
i行)与
C
j
C_{j}
Cj(
C
C
C的第
j
j
j行)的距离为:
d
(
P
i
,
C
j
)
=
(
P
i
1
−
P
j
1
)
2
+
(
P
i
2
−
P
j
2
)
2
+
⋯
+
(
P
i
D
−
P
j
D
)
2
=
(
P
i
1
2
+
P
i
2
2
+
⋯
+
P
i
D
2
)
+
(
C
j
1
2
+
C
j
2
2
+
⋯
+
C
j
D
2
)
−
2
∗
(
P
i
1
C
j
1
+
P
i
2
C
j
2
+
⋯
+
P
i
D
C
j
D
)
=
∣
∣
P
i
∣
∣
2
+
∣
∣
C
j
∣
∣
2
−
2
∗
P
i
C
j
′
d(P_i, C_j) =\sqrt{(P_{i1} - P_{j1})^2 + (P_{i2} - P_{j2})^2 + \cdots + (P_{iD} - P_{jD})^2} \\ =\sqrt{(P_{i1}^2 + P_{i2}^2 + \cdots + P_{iD}^2) + (C_{j1}^2 + C_{j2}^2 + \cdots + C_{jD}^2) - 2 * (P_{i1}C_{j1} + P_{i2}C_{j2} + \cdots + P_{iD}C_{jD})} \\ =\sqrt{||P_i||^2+||C_j||^2 - 2 * P_i C_j'}
d(Pi,Cj)=(Pi1−Pj1)2+(Pi2−Pj2)2+⋯+(PiD−PjD)2=(Pi12+Pi22+⋯+PiD2)+(Cj12+Cj22+⋯+CjD2)−2∗(Pi1Cj1+Pi2Cj2+⋯+PiDCjD)=∣∣Pi∣∣2+∣∣Cj∣∣2−2∗PiCj′
进而P与C的距离可表示为:注意下面的
\sqrt{}
作用于每一个元素
d
(
P
,
C
)
=
(
∣
∣
P
1
∣
∣
2
∣
∣
P
1
∣
∣
2
⋯
∣
∣
P
1
∣
∣
2
∣
∣
P
2
∣
∣
2
∣
∣
P
2
∣
∣
2
⋯
∣
∣
P
2
∣
∣
2
⋮
⋮
⋱
⋮
∣
∣
P
M
∣
∣
2
∣
∣
P
M
∣
∣
2
⋯
∣
∣
P
M
∣
∣
2
)
+
(
∣
∣
C
1
∣
∣
2
∣
∣
C
2
∣
∣
2
⋯
∣
∣
C
N
∣
∣
2
∣
∣
C
1
∣
∣
2
∣
∣
C
2
∣
∣
2
⋯
∣
∣
C
N
∣
∣
2
⋮
⋮
⋱
⋮
∣
∣
C
1
∣
∣
2
∣
∣
C
2
∣
∣
2
⋯
∣
∣
C
N
∣
∣
2
)
−
2
P
C
′
=
(
∣
∣
P
1
∣
∣
2
∣
∣
P
2
∣
∣
2
⋮
∣
∣
P
M
∣
∣
2
)
M
×
1
∗
(
1
1
⋯
1
)
1
×
N
+
(
1
1
⋮
1
)
M
×
1
∗
(
∣
∣
C
1
∣
∣
2
∣
∣
C
2
∣
∣
2
⋯
∣
∣
C
N
∣
∣
2
)
1
×
N
−
2
P
M
×
D
C
N
×
D
′
d(P, C) = \sqrt{ \begin{pmatrix} ||P_1||^2 && ||P_1||^2 && \cdots && ||P_1||^2 \\ ||P_2||^2 && ||P_2||^2 && \cdots && ||P_2||^2 \\ \vdots && \vdots && \ddots &&\vdots\\ ||P_M||^2 && ||P_M||^2 && \cdots && ||P_M||^2 \end{pmatrix} + \begin{pmatrix} ||C_1||^2 && ||C_2||^2 && \cdots && ||C_N||^2 \\ ||C_1||^2 && ||C_2||^2 && \cdots && ||C_N||^2 \\ \vdots && \vdots && \ddots &&\vdots\\ ||C_1||^2 && ||C_2||^2 && \cdots && ||C_N||^2 \end{pmatrix} -2PC' } \\ =\sqrt{ \begin{pmatrix} ||P_1||^2 \\ ||P_2||^2 \\ \vdots \\ ||P_M||^2 \end{pmatrix}_{M\times 1} * \begin{pmatrix} 1 && 1 && \cdots && 1 \end{pmatrix}_{1 \times N} + \begin{pmatrix} 1 \\ 1 \\ \vdots \\ 1 \end{pmatrix}_{M\times 1} * \begin{pmatrix} ||C_1||^2 && ||C_2||^2 && \cdots && ||C_N||^2 \end{pmatrix}_{1 \times N} -2P_{M\times D}C'_{N\times D} }
d(P,C)=⎝⎜⎜⎜⎛∣∣P1∣∣2∣∣P2∣∣2⋮∣∣PM∣∣2∣∣P1∣∣2∣∣P2∣∣2⋮∣∣PM∣∣2⋯⋯⋱⋯∣∣P1∣∣2∣∣P2∣∣2⋮∣∣PM∣∣2⎠⎟⎟⎟⎞+⎝⎜⎜⎜⎛∣∣C1∣∣2∣∣C1∣∣2⋮∣∣C1∣∣2∣∣C2∣∣2∣∣C2∣∣2⋮∣∣C2∣∣2⋯⋯⋱⋯∣∣CN∣∣2∣∣CN∣∣2⋮∣∣CN∣∣2⎠⎟⎟⎟⎞−2PC′=⎝⎜⎜⎜⎛∣∣P1∣∣2∣∣P2∣∣2⋮∣∣PM∣∣2⎠⎟⎟⎟⎞M×1∗(11⋯1)1×N+⎝⎜⎜⎜⎛11⋮1⎠⎟⎟⎟⎞M×1∗(∣∣C1∣∣2∣∣C2∣∣2⋯∣∣CN∣∣2)1×N−2PM×DCN×D′
之前没有想到这种no-loop的版本😂,由于one-loop完整计算一次极其缓慢(约三个小时),我利用python多进程模块multiprocessor并行加速改写了one-loop(约半小时),代码如下:
from multiprocessing import Pool
import numpy as np
import data_utils
import os
tar_name = "/home/lollows/dataset/cifar-10/cifar-10-python.tar.gz"
outpath = os.path.dirname(tar_name)
data_utils.extract_targz(tar_name, outpath)
Xtr, Ytr, Xte, Yte = data_utils.load_cifar10(os.path.join(outpath, "cifar-10-batches-py"))
# flatten out all images to be one-dimensional
Xtr_rows = Xtr.reshape(Xtr.shape[0], 32 * 32 * 3) # Xtr_rows becomes 50000 x 3072
Xte_rows = Xte.reshape(Xte.shape[0], 32 * 32 * 3) # Xte_rows becomes 10000 x 3072
def my_function(x):
dist = np.sqrt(np.sum(np.square(Xtr_rows - x), axis=1))
min_index = np.argmin(dist)
return Ytr[min_index] # do something and return something
if __name__ == '__main__':
pool = Pool(processes=6)
results = pool.map(my_function, map(lambda x: x, Xte_rows))
pool.close()
pool.join()
print('accuracy: %f' % (np.mean(results == Yte)))
# result = pool.imap(my_function, map(lambda x: x, X), chunksize=100000)
其中data_util.py文件是参照参数cs231n中的相关代码修改而来,具体如下:
import pickle
import numpy as np
import os
import tarfile
def extract_targz(tar_name, outpath=None):
tar = tarfile.open(tar_name)
tarpath = '.' if outpath is None else outpath
tar.extractall(path=tarpath)
tar.close()
print("Extract file " + tar_name + " to " + tarpath)
def load_cifar_batch(filename):
""" load single batch of cifar """
with open(filename, 'rb') as f:
datadict = pickle.load(f, encoding='latin1')
X = datadict['data']
Y = datadict['labels']
X = X.reshape(10000, 3, 32, 32).transpose(0, 2, 3, 1).astype("float")
Y = np.array(Y)
return X, Y
def load_cifar10(path):
""" load all of cifar """
xs = []
ys = []
for b in range(1, 6):
f = os.path.join(path, 'data_batch_%d' % b)
X, Y = load_cifar_batch(f)
xs.append(X)
ys.append(Y)
Xtr = np.concatenate(xs)
Ytr = np.concatenate(ys)
del X, Y
Xte, Yte = load_cifar_batch(os.path.join(path, 'test_batch'))
print("Load cifar-10 from " + path)
return Xtr, Ytr, Xte, Yte
if __name__ == "__main__":
print("===============================================")
tar_name = "/home/lollows/dataset/cifar-10/cifar-10-python.tar.gz"
outpath = os.path.dirname(tar_name)
extract_targz(tar_name, outpath)
Xtr, Ytr, Xte, Yte = load_cifar10(os.path.join(outpath, "cifar-10-batches-py"))
三、SVM与Softmax部分
这一部分的主要难点在于矩阵求导,与我们大学所学的多元函数求导有点类似,但又更近了一步。具体可参考cs231n - assignment1补充:矩阵求导,见SVM代码,Softmax代码
具体来说这部分内容中求loss比较简单,但是反向传播求gradient比较难。几个特殊函数如Max,Maximum的反向传播不是很好理解。
四、两层神经网络
因为首先会让你基于toy-data训练一个toy-model,这个一旦完成基本上可以直接用在cifar10数据集上,所以这一部分不是很难。主要是tune hyperparameters比较麻烦,不太容易实现最佳的分类效果。代码见这里
五、Features
这部分是前面SVM与Two-layer network的总结,就是把别人已经写好的Histogram of Oriented Gradients (HOG)和color histogram特征函数扔到你上面写的算法中,耐心的调一调参数就可以了。代码见这里