学习心得/总结
(1)CNN误差反向传播的细节可以参考《深度学习的数学》P200的5.5部分,还没看。task3的食物图片分类作业还在做——【李宏毅机器学习2021】卷积神经网络HW3-Image Classification。
(2)卷积层可通过重复使用卷积核有效地表征局部空间,卷积核(过滤器)通过卷积的计算结果(相似度)表示该卷积核和扫描过的图像块的灰色格子部分相吻合的个数——该值越大则说明越符合卷积核的偏好程度。
——卷积的结果矩阵为特征映射(
)
(3)说到内积,要和乘积区分,后者是将相同形状的矩阵A和B的相同位置的元素相乘,产生的矩阵。
(4)当卷积层的输入样本是三通道的彩色图像时,一开始的卷积核会是三维的,M表示卷积核大小。第二层及其以后的卷积层的输入是上一层的特征图,而特征图的个数是由上一层的卷积核数决定的。
example:当上一层的卷积核数为8时,就会得到8个特征图作为下一个层的输入,所以下一层需要8个三维的卷积核。
(4)另外:0填充(zero-padding)指用0填充输入样本的边界,填充大小为,其中F为卷积核尺寸;和多层神经网络一样,CNN中的参数训练也是使用误差反向传播算法。
文章目录
- 学习心得/总结
- 零、为啥用CNN
- 1.大部分pattern在small region
- 2.图中不同位置的same patterns
- 3.下采样(子抽样)subsampling
- 一、CNN架构
- 二、每层layer详解
- 1.Convolution
- (1)Propetry1
- (2)Propetry2
- 2.convolution vs fully connected
- 3.Max pooling
- 4.Flatten
- 三、CNN in Keras
- 1.语句介绍
- 2.Keras实现CNN栗子
- 四、CNN学到了什么
- 1.分析全连接层
- (1)分析input第一个filter
- (2)第二层的filter
- (3)分析一个filter做的事情
- (4)Degree of the activation of the k-th filter
- (5)第k个filter的作用
- 2.让图更像数字
- 五、Deep Dream
- 六、Deep style
- 七、CNN的应用
- Reference
零、为啥用CNN
CNN常被用在影像处理上,虽然也可以用一般的神经网络来处理,比如说做影像的分类——训练一个神经网络,input一张图片,将这张图片表示成里面的pixel(像素,即很长的vector了),output就是(假如有1000个类别,output就是1000个dimension)dimension。
在训练神经网络时,在network的structure里,每一个neural就代表一个最基本的classifier。根据文件上根据训练的结果,可能会出现类似的结论:举栗子:
第一层的neural是最简单的classifier,用来detain有没有绿色出现, 有没有斜的条纹等等;
而第二个layer做更复杂的东西,根据第一个layer的outpu,它看到直线横线就是窗框的一部分,看到棕色纹就是木纹,看到斜条纹+灰色可能是其他东西(轮胎的一部分等等)。根据第二个hidden layer的output,第三个hidden layer会做更加复杂的事情。
如果直接用fully connect feedforward network做影像处理时,会用到太多的参数,如一张100 ×100的彩色图,要把他拉成一个vector(100*100 3的pixel)。如果是彩色图,每个pixel像素需要3个value描述,那就是30000 ,假设
hidden layer
是有1000个neural,那这个hidden layer的参数就是30000×1000。
所以需要用CNN简化neural network的架构,咋一看CNN运作好像很复杂,事实上它的模型是比DNN还要简单的,我们就是用去把原来
中一些参数拿掉就成了CNN。
1.大部分pattern在small region
为什么我们有可能把一些参数拿掉(为什么可以用比较少的参数可以来做影像处理这件事情)
这里有几个观察,第一个是在影像处理里面,我们说第一层的 hidden layer那些neural要做的事就是侦测某一种pattern,有没有某一种patter出现。大部分的pattern其实要比整张的image还要小,对一个neural来说,假设它要知道一个image里面有没有某一个pattern出现,它其实是不需要看整张image,它只要看image的一小部分。
举例,上图中第一个hidden layer的某一种neural的工作就是要侦测有没有鸟嘴的存在(有一些neural侦测有没有爪子的存在,有没有一些neural侦测有没有翅膀的存在,有没有尾巴的存在,合起来就可以侦测图片中某一只鸟)。假设有一个neural的工作是要侦测有没有鸟嘴的存在,那并不需要看整张图,其实我们只需要给neural看着一小红色方框的区域(鸟嘴),它其实就可以知道说,它是不是一个鸟嘴。对人来说也是一样,看这一小块区域这是鸟嘴,不需要去看整张图才知道这件事情。所以,每一个neural连接到每一个小块的区域就好了,不需要连接到整张完整的图。
2.图中不同位置的same patterns
第二个观察是这样子,同样的 在
里面,可能会出现在
不同的部分,但是代表的是同样的含义,它们有同样的形状,可以用同样的
,同样的参数就可以把
侦测出来。
比如说,这张图里面有一张在左上角的鸟嘴,在这张图里面有一个在中央的鸟嘴,但是你并不需要说:我们不需要去训练两个不同的detector,一个专门去侦测左上角的鸟嘴,一个去侦测中央有没有鸟嘴。如果这样做的话,这样就太冗了。我们不需要太多的冗源,这个nerual侦测左上角的鸟嘴跟侦测中央有没有鸟嘴做的事情是一样的。我们并不需要两个neural去做两组参数,我们就要求这两个neural用同一组参数,就样就可以减少你需要参数的量
3.下采样(子抽样)subsampling
第三个是:我们知道一个image你可以做subsampling,你把一个image的奇数行,偶数列的pixel拿掉,变成原来十分之一的大小,它其实不会影响人对这张image的理解。对你来说:这张image跟这张image看起来可能没有太大的差别。是没有太大的影响的,所以我们就可以用这样的概念把image变小,这样就可以减少你需要的参数。
一、CNN架构
首先input一张image以后,这张image会通过convolution layer,接下里做max pooling这件事,然后在做convolution,再做max pooling这件事。这个process可以反复无数次,反复的次数你觉得够多之后,(但是反复多少次你是要事先决定的,它就是network的架构(就像你的neural有几层一样),你要做几层的convolution,做几层的Max Pooling,你在定neural架构的时候,你要事先决定好)。你做完决定要做的convolution和Max Pooling以后,你要做另外一件事,这件事情叫做flatten,再把flatten的output丢到一般fully connected feedforward network,然后得到影像辨识的结果。
我们刚才讲基于三个对影像处理的观察,所以设计了CNN这样的架构。
- 第一个,要生成一个pattern,不要看整张的image,你只需要看image的一小部分。
- 第二个,通用的pattern会出现在一张图片的不同的区域。
- 第三个,可以做subsampling
前面的两个property可以用convolution来处理掉,最后的property可以用Max Pooling这件事来处理。等一下我们要介绍每一个layer再做的事情,我们就先从convolution开始看起。
二、每层layer详解
1.Convolution
(1)Propetry1
假设现在我们的network的input是一张的Image,如果是黑白的,一个pixel就只需要用一个value去描述它,1就代表有涂墨水,0就代表没有涂到墨水。那在convolution layer里面,它由一组的filter,(其中每一个filter其实就等同于是fully connect layer里面的一个neuron),每一个filter其实就是一个matrix(3 *3),这每个filter里面的参数(matrix里面每一个element值)就是network的parameter(这些parameter是要学习出来的,并不是需要人去设计的)
每个filter如果是3* 3的detects意味着它就是再侦测一个3 *3的pattern(看3 *3的一个范围)。在侦测pattern的时候不看整张image,只看一个3 *3的范围内就可以决定有没有某一个pattern的出现。
(2)Propetry2
这个filter咋样跟这个image运作呢?首先第一个filter是一个3* 3的matrix,把这个filter放在image的左上角,把filter的9个值和image的9个值做内积,两边都是1,1,1(斜对角),内积的结果就得到3。移动的距离叫做(
等于多少,自己来设计),内积(矩阵对应的每个元素逐个相乘)等于-1。stride等于2,内积等于-3。我们先设stride等于1。
你把filter往右移动一格得到-1,再往右移一格得到-3,再往右移动一格得到-1。接下里往下移动一格,得到-3。以此类推(每次都移动一格),直到你把filter移到右下角的时候,得到-1(得到的值如图所示)
经过这件事情以后,本来是的matrix,经过convolution process就得到
的matrix。如果你看filter的值,斜对角的值是1,1,1。所以它的工作就是detain1有没有1,1,1(连续左上到右下的出现在这个image里面)。比如说:出现在这里(如图所示蓝色的直线),所以这个filter就会告诉你:左上跟左下出现最大的值。
就代表说这个filter要侦测的pattern,出现在这张image的左上角和左下角,这件事情就考虑了propetry2。同一个pattern出现在了左上角的位置跟左下角的位置,我们就可以用filter 1侦测出来,并不需要不同的filter来做这件事。
在一个convolution layer 里面会有很多的filter(刚才只是一个filter的结果),那另外的filter会有不同的参数(图中显示的filter2),它也做跟filter1一模一样的事情,在filter放到左上角再内积得到结果-1,依次类推。你把filter2跟 input image做完 之后,你就得到了另一个4 * 4的matrix,上图中的红色
的
跟蓝色的matrix合起来就叫做
,看你有几个filter,你就得到多少个image(你有100个filter,你就得到100个4 * 4的image,组成
,注意:如果像下面的彩色RGB图片,一个filter指一个立方体)。
刚才举的例子是一张黑白的image,所以input是一个matrix。若今天换成彩色的image,因为彩色的image是由RGB组成的,所以,一个彩色的image就是好几个matrix叠在一起,就是一个立方体。如果要处理彩色image,这时候filter不是一个matrix,filter而是一个立方体(即每个卷积层中有3个卷积核)。如果今天是RGB表示一个pixel的话,那input就是3*6 *6(此处第一维度为channel),那filter就是3 *3 *3。
这里我们说的彩色图片在高和宽2个维度外还有RGB(红、绿、蓝)三种颜色通道,假设彩色图片的高和宽分别是h和w(像素),那么该图片可以表示为一个3×h×w的多维数组。我们将大小为3的这一维称为通道(channel)维。
在做convolution的话,就是将filter的9个值和image的9个值做内积(不是把每一个channel分开来算,而是合在一起来算,一个filter就考虑了不同颜色所代表的channel)
2.convolution vs fully connected
就是fully connected layer把一些weight拿掉了。经过convolution的output其实就是一个hidden layer的neural的output。如果把这两个link在一起的话,convolution就是fully connected拿掉一些weight的结果。
我们在做convolution的时候,我们filter1放到左上角(先考虑filter1),然后做inner product,得到内积为3,这件事情就等同于把6* 6的image拉直(变成如图所示)。然后你有一个neural的output是3,这个neural的output考虑了9个pixel,这9个pixel分别就是编号(1,2,3,7,8,9,1 3,14,15)的pixel。这个filter做inner product以后的output 3就是某个neuron output 3时,就代表这个neuron的weight只连接到(1,2,3,7,8,9,13,14,15)。这9个weight就是filter matrix里面的9个weight(同样的颜色)在fully connected中,一个neural应该是连接在所有的input(有36个pixel当做input,这个neuron应连接在36个input上),但是现在只连接了9个input(detain一个pattern,不需要看整张image,看9个input就好),这样做就是用了比较少的参数了。
将stride=1(移动一格)做内积得到另外一个值-1,假设这个-1是另外一个neural的output,这个neural连接到input的(2,3,4,8,9,10,14,15,16),同样的weight代表同样的颜色。
这两个neuron本来就在fully connect里面这两个neural本来是有自己的weight,当我们在做convolution时,首先把每一个neural连接的wight减少,强迫这两个neural共用一个weight(shared weight)。
3.Max pooling
相对于convolution来说,Max Pooling是比较简单的。我们根据filter 1得到4*4的maxtrix,根据filter2得到另一个4 *4的matrix,接下来把output ,4个一组。每一组里面可以选择它们的平均或者选最大的都可以,就是把四个value合成一个value。这个可以让你的image缩小。选完后的结果如下图所示:
假设我们选择四个里面的max vlaue保留下来,这样可能会有个问题,把这个放到neuron里面,这样就不能够微分了,但是可以用微分的办法来处理的
做完一个convolution和一次max pooling,就将原来6 * 6的image变成了一个2 *2的image。这个2 *2的pixel的深度depend你有几个filter(你有50个filter你就有50维),得到结果就是一个new image but smaller,一个filter就代表了一个channel。
这件事可以repeat很多次,通过一个convolution + max pooling就得到新的 image。它是一个比较小的image,可以把这个小的image,做同样的事情,再次通过convolution + max pooling,将得到一个更小的image。
这边有一个问题:第一次有25个filter,得到25个feature map,第二个也是由25个filter,那将其做完是不是要得到的feature map。其实不是这样的!
每个filter都会对前面的所有层内积求和
假设第一层filter有2个,第二层的filter在考虑这个input时是会考虑深度的,并不是每个channel分开考虑,而是一次考虑所有的channel。所以convolution有多少个filter,output就有多少个filter(convolution有25个filter,output就有25个filter。只不过,这25个filter都是一个立方体cube)
4.Flatten
flatten就是feature map拉直,拉直之后就可以丢到fully connected feedforward netwwork,然后就结束了。
最后我们可以通过一个动态图复习这个过程:
三、CNN in Keras
1.语句介绍
唯一要改的是:network structure和input format,本来在DNN中input是一个vector,现在是CNN的话,会考虑 input image的几何空间的,所以不能给它一个vector。应该input一个tensor(高维的vector)。为什么要给三维的vector?因为image的长宽高各是一维,若是彩色的话就是第三维。所以要给三维的tensor
model.add(Convolution2D(25, 3, 3))
25代表有25个filter,3 *3代表filter是一个3 *3的matrix(在二维卷积中,卷积核大小通常设置为 3×3)。
Input_shape=(28,28,1)
假设我要做手写数字辨识,input是28 *28的image,每个pixel都是单一颜色。所以input_shape
是(1,28,28)。如果是黑白图为1(blacj/white),如果是彩色的图时为3(每个pixel用三个值来表述)。
MaxPooling2D(( 2, 2 ))
2,2表示把2*2的feature map里面的pixel拿出来,选择max value
假设我们input一个1 *28 * 28的image,你就可以写model.add(Convolution2D( 25, 3, 3, Input_shape=(28,28,1)))
。通过convplution以后得到output是25 *26 26(25个filter,通过3 *3得到26 * 26)。然后做max pooling,2 *2一组选择 max value得到 25 *13 * 13
然后在做一次convolution,假设我在这选50个filter,每一个filter是3 *3时,那么现在的channel就是50。13 *13的image通过3 *3的filter,就成11 *11,然后通过2 *2的Max Pooling,变成了50 *5 *5
在第一个convolution layer里面,每一个filter有9个参数,在第二个convolution layer里面,虽然每一个filter都是3 *3,但不是3 *3个参数,因为它input channel 是25个,所以它的参数是3 *3 *25(225)。
通过两次convolution,两次Max Pooling,原来是1 *28 *28变为50 *5 *5。flatten的目的就是把50 *5 *5拉直,拉直之后就成了1250维的vector,然后把1250维的vector丢到fully connected。
2.Keras实现CNN栗子
在Keras英文官方文档(https://keras.io/examples/vision/mnist_convnet/)上,fchollet用CNN识别MNIST手写识别体
# 导入对应的库
import numpy as np
from tensorflow import keras
from tensorflow.keras import layers
# 准备数据
# Model / data parameters
num_classes = 10
input_shape = (28, 28, 1)
# the data, split between train and test sets
(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
# Scale images to the [0, 1] range
x_train = x_train.astype("float32") / 255
x_test = x_test.astype("float32") / 255
# Make sure images have shape (28, 28, 1)
x_train = np.expand_dims(x_train, -1)
x_test = np.expand_dims(x_test, -1)
print("x_train shape:", x_train.shape)
print(x_train.shape[0], "train samples")
print(x_test.shape[0], "test samples")
# convert class vectors to binary class matrices
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
上面代码中keras.utils.to_categorical
函数将类别标签转换为onehot编码:
x_train shape: (60000, 28, 28, 1)
60000 train samples
10000
建立模型:
# 建立模型
model = keras.Sequential(
[
keras.Input(shape=input_shape),
layers.Conv2D(32, kernel_size=(3, 3), activation="relu"),
layers.MaxPooling2D(pool_size=(2, 2)),
layers.Conv2D(64, kernel_size=(3, 3), activation="relu"),
layers.MaxPooling2D(pool_size=(2, 2)),
layers.Flatten(),
layers.Dropout(0.5),
layers.Dense(num_classes, activation="softmax"),
]
)
model.summary()
Keras的model.summary()方便地将model的每层shape和parameters都打印出来:
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d (Conv2D) (None, 26, 26, 32) 320
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 13, 13, 32) 0
_________________________________________________________________
conv2d_1 (Conv2D) (None, 11, 11, 64) 18496
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 5, 5, 64) 0
_________________________________________________________________
flatten (Flatten) (None, 1600) 0
_________________________________________________________________
dropout (Dropout) (None, 1600) 0
_________________________________________________________________
dense (Dense) (None, 10) 16010
=================================================================
Total params: 34,826
Trainable params: 34,826
Non-trainable params: 0
训练模型:
# 训练模型model
batch_size = 128
epochs = 15
model.compile(loss="categorical_crossentropy", optimizer="adam", metrics=["accuracy"])
model.fit(x_train, y_train, batch_size=batch_size, epochs=epochs, validation_split=0.1)
从而观察每个epoch的accuracy和loss值:
Epoch 1/15
422/422 [==============================] - 27s 65ms/step - loss: 0.3848 - accuracy: 0.8804 - val_loss: 0.0834 - val_accuracy: 0.9790
Epoch 2/15
422/422 [==============================] - 29s 68ms/step - loss: 0.1146 - accuracy: 0.9655 - val_loss: 0.0594 - val_accuracy: 0.9832
Epoch 3/15
422/422 [==============================] - 27s 64ms/step - loss: 0.0857 - accuracy: 0.9736 - val_loss: 0.0482 - val_accuracy: 0.9867
Epoch 4/15
422/422 [==============================] - 29s 69ms/step - loss: 0.0726 - accuracy: 0.9775 - val_loss: 0.0441 - val_accuracy: 0.9878
Epoch 5/15
422/422 [==============================] - 27s 63ms/step - loss: 0.0628 - accuracy: 0.9803 - val_loss: 0.0391 - val_accuracy: 0.9900
Epoch 6/15
422/422 [==============================] - 29s 69ms/step - loss: 0.0564 - accuracy: 0.9824 - val_loss: 0.0385 - val_accuracy: 0.9898
Epoch 7/15
422/422 [==============================] - 27s 64ms/step - loss: 0.0511 - accuracy: 0.9838 - val_loss: 0.0353 - val_accuracy: 0.9893
Epoch 8/15
422/422 [==============================] - 27s 65ms/step - loss: 0.0470 - accuracy: 0.9854 - val_loss: 0.0323 - val_accuracy: 0.9915
Epoch 9/15
422/422 [==============================] - 25s 60ms/step - loss: 0.0448 - accuracy: 0.9855 - val_loss: 0.0322 - val_accuracy: 0.9915
Epoch 10/15
422/422 [==============================] - 26s 62ms/step - loss: 0.0410 - accuracy: 0.9872 - val_loss: 0.0331 - val_accuracy: 0.9912
Epoch 11/15
422/422 [==============================] - 25s 59ms/step - loss: 0.0400 - accuracy: 0.9877 - val_loss: 0.0314 - val_accuracy: 0.9918
Epoch 12/15
422/422 [==============================] - 25s 60ms/step - loss: 0.0378 - accuracy: 0.9876 - val_loss: 0.0280 - val_accuracy: 0.9920
Epoch 13/15
422/422 [==============================] - 25s 59ms/step - loss: 0.0359 - accuracy: 0.9892 - val_loss: 0.0289 - val_accuracy: 0.9910
Epoch 14/15
422/422 [==============================] - 25s 60ms/step - loss: 0.0351 - accuracy: 0.9892 - val_loss: 0.0279 - val_accuracy: 0.9915
Epoch 15/15
422/422 [==============================] - 26s 62ms/step - loss: 0.0319 - accuracy: 0.9899 - val_loss: 0.0268 - val_accuracy: 0.9938
<tensorflow.python.keras.callbacks.History at 0x2d562a5de50>
最后的评估模型,收敛后,可看出accuracy还是很高的:
# 评估模型
score = model.evaluate(x_test, y_test, verbose=0)
print("Test loss:", score[0])
print("Test accuracy:", score[1])
# Test loss: 0.025853432714939117
# Test accuracy: 0.9916999936103821
四、CNN学到了什么
1.分析全连接层
很多人常会说:deep learning就是一个黑盒子,然后你learn以后你不知道它得到了什么,所以有很多人不喜欢用这种方法。但还有很多的方法分析的,比如说我们今天来示范一下咋样分析CNN,它到底学到了什么。
(1)分析input第一个filter
分析input第一个filter是比较容易的,因为一个layer每一个filter就是一个3*3的mmatrix,对应到3 *3的范围内的9个pixel。所以你只要看到这个filter的值就可以知道说:它在detain什么东西,所以第一层的filter是很容易理解的,
(2)第二层的filter
但是你没有办法想要它在做什么事情的是第二层的filter。在第二层我们也是3 *3的filter有50个,但是这些filter的input并不是pixel(3 *3的9个input不是pixel)。而是做完convolution再做Max Pooling的结果。所以这个3 *3的filter就算你把它的weight拿出来,你也不知道它在做什么。另外这个3 *3的filter它考虑的范围并不是3 *3的pixel(9个pixel),而是比9个pxiel更大的范围。不要这3 *3的element的 input是做完convolution再加Max Pooling的结果。所以它实际上在image上看到的范围,是比3 *3还要更大的。那我们咋样来分析一个filter做的事情是什么呢,以下是一个方法。
(3)分析一个filter做的事情
我们知道现在做第二个convolution layer里面的50个filter,每一个filter的output就是一个matrix(11*11的matrix)。假设我们现在把第k个filter拿出来,它可能是这样子的(如图),每一个element我们就叫做(上标是说这是第k个filter,i,j代表在这个matrix里面的第i row和第j column)。
(4)Degree of the activation of the k-th filter
接下来我们定义一个东西叫做:“Degree of the activation of the k-th filter”,我们定义一个值代表说:现在第k个filter有多被active(现在的input跟第k个filter有多match),第k个filter被启动的Degree定义成:这个11*11的 matrix里面全部的 element的summation。(input一张image,然后看这个filter output的这个11 *11的值全部加起来,当做是这个filter被active的程度)
(5)第k个filter的作用
我们想知道第k个filter的作用是什么,所以我们想要找一张image,这张image它可以让第k个filter被active的程度最大。
假设input一张image,我们称之为X,那我们现在要解的问题就是:找一个x,它可以让我们现在定义的activation Degree 最大,这件事情要咋样做到呢?其实是用gradient ascent你就可以做到这件事(minimize使用gradient descent,maximize使用gradient ascent)
这是事还是蛮神妙的,我们现在是把X当做我们要找的参数用gradient ascent做update,原来在train CNN network neural的时候,input是固定的,model的参数是你需要用gradient descent找出来的,用gradient descent找参数可以让loss被 minimize。但是现在立场是反过来的,现在在这个task里面,model的参数是固定的,我们要让gradient descent 去update这个X,可以让这个activation function的Degree of the activation是最大的。
2.让图更像数字
我们有没有办法让这个图看起来更像数字呢?想法是这样的:一张图是不是数字我们有一些基本的假设,比如说:这些就算你不知道它是什么数字(显然它不是digit),人类手写出来的就不是这个样子。所以我们应该对x做constraint,我们告诉machine,有些x可能会使y很大但不是数字。我们根据人的power-knowledge就知道,这些x不可能是一些数字。那么我们可以加上咋样的constraint呢?(图中白色的亮点代表的是有墨水的,对一个digit来说,图白的部分其实是有限的,对于一个数字来说,一整张图的某一个小部分会有笔画,所以我们应该对这个x做一些限制)
假设image里面的每一个pixel用来表示,(每一个image有28 *28的pixel)我们把所有image上
的值取绝对值后加起来。如果你熟悉machine learning的话,这一项就是L1-regularization。然后我们希望说:在找一个x可以让
最大的同时让
的summation越小越好。也就是我们希望找出的image,大部分的地方是没有涂颜色的,只有非常少的部分是有涂颜色的。如果我们加上constraint以后我们得到的结果是这样的(如右图所示),跟左边的图比起来,隐约可以看出来它是一个数字(得到的结果看起来像数字)
你可能会有一个问题,绝对值咋样去微分,下堂课会讲到
你如果加上一些额外的constraint,比如说:你希望相邻的pixel
是同样的颜色等等,你应该可以得到更好的结果。不过其实有更多很好的方法可以让machine generate数字
五、Deep Dream
其实上述的想法就是Deep Dream的精神,Deep Dream是说:如果你给machine一张image,它会在这张image里加上它看到的东西。咋样做这件事情呢?你先找一张image,然后将这张image丢到CNN中,把它的某一个hidden layer拿出来(vector),它是一个vector(假设这里是:[3.9, -1.5, 2.3…])。接下来把postitive dimension值调大,把negative dimension值调小(正的变的更正,负的变得更负)。你把这个(调节之后的vector)当做是新的image的目标(把3.9的值变大,把-1.5的值变得更负,2.3的值变得更大。然后找一张image(modify image)用GD方法,让它在hidden layer output是你设下的target)。这样做的话就是让CNN夸大化它所看到的东西,本来它已经看到某一个东西了,你让它看起来更像它原来看到的东西。本来看起来是有一点像东西,它让某一个filter有被active,但是你让它被active的更剧烈(夸大化看到的东西)。
如果你把这张image拿去做Deep Dream的话,你看到的结果是这样子的。右边有一只熊,这个熊原来是一个石头(对机器来说,这个石头有点像熊,它就会强化这件事情,所以它就真的变成了一只熊)。Deep Dream还有一个进阶的版本,叫做Deep Style
六、Deep style
今天input一张image,input一张image,让machine去修改这张图,让它有另外一张图的风格 (类似于风格迁移)
这里给一个reference给参考论文 其中做法的精神是这样的:原来的image丢给CNN,然后得到CNN的filter的output,CNN的filter的output代表这张image有什么content。接下来你把呐喊这张图也丢到CNN里面,也得到filter的output。我们并不在意一个filter ,而是在意filter和filter之间
的convolution,这个convolution代表了这张image的style。
接下来你用同一个CNN找一张image,这张image它的content像左边这张相片,但同时这张image的style像右边这张相片。你找一张image同时可以maximize左边的图,也可以maximize右边的图。那你得到的结果就是像最底下的这张图。用的就是刚才讲的gradient ascent的方法找一张image,然后maximize这两张图,得到就是底下的这张图。
七、CNN的应用
围棋、AlphaGo、语音、文本
什么时候应该用CNN呢?image必须有该有的那些特性,在CNN开头就有说:根据那三个观察,所以设计出了CNN这样的架构。在处理image时是特别有效的。为什么这样的架构也同样可以用在围棋上面(因为围棋有一些特性跟影像处理是非常相似的)
- 第一个是:在image上面,有一些pattern是要比整张image还要小的多的(比如:鸟喙是要比整张的image要小的多),只需要看那一小的部分就知道那是不是鸟喙。在围棋上也有同样的现象,如图所示,一个白子被三个黑子围住(这就是一个pattern),你现在只需要看这一小小的范围,就可以知道白子是不是没“气”了,不需要看整个棋盘才能够知道这件事情,这跟image是有同样的性质。
- 在“AlphaGo”里面它的第一个layer filter其实就是用5*5的filter,显然做这个设计的人觉得说:围棋最基本的pattern可能都是在5 *5的范围内就可以被侦测出来,不需要看整个棋牌才能知道这件事情。
- 接下来我们说image还有一个特性:同样的pattern会出现在不同的regions,而他们代表的是同样的意义,在围棋上可能也会有同样的现象。像如图这个pattern可以出现在左上角,也可以出现在右下角,它们都代表了同样的意义。所以你可以用同一个pattern来处理在不同位置的同样的pattern。所以对围棋来说,是有这两个特性的。
Reference
(1)datawhale李宏毅学习笔记
(2)keras官方文档:https://keras.io/examples/vision/mnist_convnet/
(3)更多参考:
如果你想要用Degree的方法来让machine自动产生一个digit,这件事是不太成功的,但是有很多其它的方法,可以让machine画出非常清晰的图。这里列了几个方法,比如说:PixelRNN,VAE,GAN来給参考.