中英文对照翻译:https://blog.csdn.net/c_chuxin/article/details/82856502
总结参考:
https://blog.csdn.net/weixin_43624538/article/details/84863685
https://blog.csdn.net/mao_xiao_feng/article/details/53350798
代码参考:https://blog.csdn.net/shankezh/article/details/88311788
重点:
A fundamental way of solving both of these issues would be to introduce sparsity and replace the fully connected layers by the sparse ones, even inside the convolutions. Besides mimicking biological systems, this would also have the advantage of firmer theoretical underpinnings due to the groundbreaking work of Arora et al. [2]. Their main result states that if the probability distribution of the dataset is representable by a large, very sparse deep neural network, then the optimal network topology can be constructed layer after layer by analyzing the correlation statistics of the preceding layer activations and clustering neurons with highly correlated outputs. Although the strict mathematical proof requires very strong conditions, the fact that this statement resonates with the well known Hebbian principle —— neurons that fire together, wire together —— suggests that the underlying idea is applicable even under less strict conditions, in practice.
解决这两个问题的一个基本的方式就是将全连接层替换为稀疏的全连接层,甚至在卷积层内部。除了模仿生物系统之外,由于Arora等人的开创性工作,这也将具有更坚实的理论基础的优势[2]。它们的主要结果是,如果数据集的概率分布可以用一个大的、非常稀疏的深层神经网络来表示, 则最优的网络拓扑结构可以通过分析前一层激活的相关性统计和聚类高度相关的神经元来一层层的构建。尽管严格的数学证明需要很强的条件,但这一说法与众所周知的Hebbian原理产生了共鸣-神经元一起激发、一起连接-这表明,即使在实际中,在不太严格的条件下,这种基本思想也是适用的。
Hebbian principle
目前图像领域的深度学习,是使用更深的网络提升representation power,从而提高准确率,但是这会导致网络需要更新的参数爆炸式增长,导致两个严重的问题:
1、网络更容易过拟合,当数据集不全的时候,过拟合更容易发生,于是我们需要为网络feed大量的数据,但是制作样本集本身就是一件复杂的事情。
2、大量需要更新的参数就会导致需要大量的计算资源,而当下即使硬件快速发展,这样庞大的计算也是很昂贵的
解决以上问题的根本方法就是把全连接的网络变为稀疏连接(卷积层其实就是一个稀疏连接),当某个数据集的分布可以用一个稀疏网络表达的时候就可以通过分析某些激活值的相关性,将相关度高的神经元聚合,来获得一个稀疏的表示。
这种方法也呼应了Hebbian principle,一个很通俗的现象,先摇铃铛,之后给一只狗喂食,久而久之,狗听到铃铛就会口水连连。这也就是狗的“听到”铃铛的神经元与“控制”流口水的神经元之间的链接被加强了,而Hebbian principle的精确表达就是如果两个神经元常常同时产生动作电位,或者说同时激动(fire),这两个神经元之间的连接就会变强,反之则变弱(neurons that fire together, wire together)
四、Inception结构
通过上述的分析,作者提出了Inception结构,这是一种高效表达特征的稀疏性结构。基于底层的相关性高的单元,通常会聚集在图像的局部区域(因为通常CNN底层卷积提取的都是局部特征),这就相当于在单个局部区域上,我们去学习它的特征,然后在高层用1*1卷积代替这个区域,当然某些相关性可能是隔得比较远的,通过使用大的卷积核学习即可。
最后就是这样的一个结构:
模型结构:
代码(命名冲突那里bug了挺久):
import tensorflow as tf
import cv2
import numpy as np
import os
from PIL import Image
from sklearn.model_selection import train_test_split
class InceptionV1(object):
def __init__(self):
self.n_input = 224*224*3
self.n_classes = 2
self.batch_size = 10
self.training_iters = 20
self.display_step = 20
self.learning_rate = 0.001
def conv2d(self,x,filters,k_size,strides=[1,1],padding='SAME',activation = tf.nn.relu,scope='conv2d'):
return tf.layers.conv2d(inputs=x,filters=filters,kernel_size=k_size,
strides=strides,padding=padding,name=scope,activation=activation)
def maxpool2d(self,x,pool_size=[2,2],strides=[2,2],padding='same',scope='conv2d'):
return tf.layers.max_pooling2d(inputs=x,pool_size=pool_size,strides=strides,padding=padding,name=scope)
def dropoutx(self,x,d_rate):
return tf.layers.dropout(x,rate=d_rate)
def avgpool2d(self,x,pool_size=[1,1],strides=[1,1],padding='same',scope='avgpool2d'):
return tf.layers.average_pooling2d(x,pool_size=pool_size,strides=strides,padding=padding,name=scope)
def inception_moudle_v1(self,x,filters_num,scope='layers'):
with tf.variable_scope(scope):
# tf.reset_default_graph()
bh1 = self.conv2d(x,filters=filters_num[0],k_size=[1,1],strides=[1,1],scope=scope+'1')
bh2 = self.conv2d(x,filters=filters_num[1],k_size=[1,1],strides=[1,1],scope=scope+'2')
bh2 = self.conv2d(bh2,filters=filters_num[2],k_size=[3,3],strides=[1,1],scope=scope+'3')
bh3 = self.conv2d(x,filters=filters_num[3],k_size=[1,1],strides=[1,1],scope=scope+'4')
bh3 = self.conv2d(bh3,filters=filters_num[4],k_size=[5,5],strides=[1,1],scope=scope+'5')
bh4 = self.maxpool2d(x,pool_size=[3,3],strides=[1,1])
bh4 = self.conv2d(bh4,filters=filters_num[5],k_size=[1,1],strides=[1,1],scope=scope+'6')
return tf.concat([bh1,bh2,bh3,bh4],axis=3)
def set_net(self,x,keep_prob,is_train=True):
check_point = {}
net = tf.reshape(x,[-1,224,224,3])
print(net)
net = self.conv2d(net,filters=64,k_size=[7,7],strides=[2,2],scope='conv1_1')
net = self.maxpool2d(net,pool_size=[3,3],scope='pool1')
net = self.conv2d(net,filters=64,k_size=[1,1],strides=[1,1],scope='conv2_1')
net = self.conv2d(net,filters=192,k_size=[3,3],strides=[1,1],scope='conv2_2')
net = self.maxpool2d(net,pool_size=[3,3],scope='pool2')
net = self.inception_moudle_v1(net,[64,96,128,16,32,32],scope='layer1')
net = self.inception_moudle_v1(net,[128,128,192,32,96,64],scope='layer2')
net = self.maxpool2d(net,pool_size=[3,3],scope='pool3')
net = self.inception_moudle_v1(net, [192, 96, 208, 16, 48, 64],scope='layer3')
net = self.inception_moudle_v1(net, [160, 112, 224, 24, 64, 64],scope='layer4')
net_1 = net
net = self.inception_moudle_v1(net, [128, 18, 256, 24, 64, 64],scope='layer5')
net = self.inception_moudle_v1(net, [112, 144, 288, 32, 64, 64],scope='layer6')
net_2 = net
net = self.inception_moudle_v1(net, [256, 160, 320, 32, 128, 128],scope='layer7')
net = self.maxpool2d(net, pool_size=[3,3], scope='pool4')
net = self.inception_moudle_v1(net, [256, 160, 320, 32, 128, 128],scope='layer8')
net = self.inception_moudle_v1(net, [384, 192, 384, 48, 128, 128],scope='layer9')
net = self.avgpool2d(net, pool_size=[7,7], strides=[1,1], padding='VALID', scope='layer26')
net = self.dropoutx(net,d_rate=keep_prob)
net = self.conv2d(net, filters=2, k_size=[1,1],strides=[1,1], activation=None, scope='layer27')
net = tf.squeeze(net, [1, 2], name='squeeze')
# 该函数返回一个张量,这个张量是将原始input中所有维度为1的那些维都删掉的结果
# axis可以用来指定要删掉的为1的维度,此处要注意指定的维度必须确保其是1,否则会报错
# # 't' 是一个维度是[1, 2, 1, 3, 1, 1]的张量
# tf.shape(tf.squeeze(t)) # [2, 3], 默认删除所有为1的维度
# # 't' 是一个维度[1, 2, 1, 3, 1, 1]的张量
# tf.shape(tf.squeeze(t, [2, 4])) # [1, 2, 3, 1],标号从零开始,只删掉了2和4维的1
net = tf.nn.softmax(net)
if is_train:
net_1 = self.avgpool2d(net_1, pool_size=[5,5], padding='VALID', strides=[1,1], scope='auxiliary0_avg')
net_1 = self.conv2d(net_1, filters=128, k_size=[1,1],strides=[1,1],scope='auxiliary0_conv_1X1')
net_1 = tf.layers.flatten(net_1)
net_1 = tf.layers.dense(net_1, 1024)
net_1 = self.dropoutx(net_1, 0.7)
net_1 = tf.layers.dense(net_1, 2,activation=None)
net_1 = tf.nn.softmax(net_1)
net_2 = self.avgpool2d(net_2, pool_size=[5,5], padding='VALID', strides=[3,3], scope='auxiliary1_avg')
net_2 = self.conv2d(net_2, filters=128, k_size=[1,1],strides=[1,1],scope='auxiliary1_conv_1X1')
net_2 = tf.layers.flatten(net_2)
net_2 = tf.layers.dense(net_2, 1024)
net_2 = self.dropoutx(net_2, 0.7)
net_2 = tf.layers.dense(net_2, 2, activation=None)
net_2 = tf.nn.softmax(net_2)
net = net_1 * 0.3 + net_2 * 0.3 + net * 0.4
return net
def inceptionv1_prediction(self, X,Y, scope='vgg'):
X_train, X_vaild, y_train, y_vaild = train_test_split(X, Y, test_size=0.2)
x = tf.placeholder(tf.float32, [None, self.n_input])
y = tf.placeholder(tf.float32, [None, self.n_classes])
pred = self.set_net(x,0.8) # pred是计算完的值,此时还没归一化
a = tf.nn.softmax(pred) # a是归一化后的值。
# 定义损失函数和学习步骤
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=pred, labels=y)) # 这个是损失loss
optimizer = tf.train.AdamOptimizer(learning_rate=self.learning_rate).minimize(cost) # 最小化loss
# 测试网络
correct_pred = tf.equal(tf.argmax(pred, 1), tf.argmax(y, 1))
accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))
# 初始化所有的共享变量
init = tf.initialize_all_variables()
with tf.Session() as sess:
sess.run(init)
step = 1
# Keep training until reach max iterations
while step * self.batch_size < self.training_iters: # 直到达到最大迭代次数,没考虑梯度!!!
batch_xs, batch_ys = X_train[self.batch_size*(step-1):self.batch_size*step],y_train[self.batch_size*(step-1):self.batch_size*step]
batch_xs = np.reshape(batch_xs,(-1,self.n_input))
print('~!!!!!!!!',batch_xs.shape)
print(batch_ys.shape)
# 获取批数据
sess.run(optimizer, feed_dict={x: batch_xs, y: batch_ys})
if step % self.display_step == 0: # 每一步里有64batch,64*20=1280
# 计算精度
acc = sess.run(accuracy, feed_dict={x: batch_xs, y: batch_ys})
# 计算损失值
loss = sess.run(cost, feed_dict={x: batch_xs, y: batch_ys})
print("Iter " + str(step * self.batch_size) + ", Minibatch Loss= " + "{:.6f}".format(
loss) + ", Training Accuracy = " + "{:.5f}".format(acc))
step += 1
print("Optimization Finished!")
# 计算测试精度
X_vaild = np.reshape(X_vaild, (-1, self.n_input))
print("Testing Accuracy:", sess.run(accuracy,
feed_dict={x: X_vaild[:256], y: y_vaild[:256],
})) # 拿前256个来测试
print("Testing Result:", sess.run(a, feed_dict={x: X_vaild[63:64], y: y_vaild[63:64],
})) # 数组范围,从0开始,含左不含右