閒來無事(好像也不閒啊)想從零開始構建一個model在mnist 上超過lenet5而且只訓練1000steps,利用這兩年看的論文,還是有信心的,畢竟lenet5 是20年前的模型啊!
首先看下baseline:
emmmm,還是很高的。
先說說想法: 我想搞個 inception 那樣的block 爲啥呢因爲我覺着 1.一個block裏花樣多一些有利於提取不同尺度下的特徵,而且某種角度上看 inception就是resnet的跨層連接的變種。
首先構建block:
def block1(x,block_name):
mw1=get_weight_variable([3,3,1,32],block_name+"_mW1weights")
mw21=get_weight_variable([1,5,1,32],block_name+"_mW2weights1")
mw22=get_weight_variable([5,1,32,32],block_name+"_mW2weights2")
mb2=get_bias_variable([32],block_name+"_mb2")
mwn=get_weight_variable([3,3,33,32],block_name+"_mWnweights")
module1_out=conv2d(x,mw1)
module2_out=conv2d(x,mw21)
module2_out=conv2d(module2_out,mw22)
module2_out+=mb2
module3_out=pooling2d(x,[1,5,5,1],[1,1,1,1])
module_out=module1_out+module2_out
module_out=tf.concat([module_out,module3_out],3)
return conv2d(module_out,mwn)
def block2(x,block_name):
mw1=get_weight_variable([3,3,32,64],block_name+"_mW1weights")
mw21=get_weight_variable([1,5,32,64],block_name+"_mW2weights1")
mw22=get_weight_variable([5,1,64,64],block_name+"_mW2weights2")
mb2=get_bias_variable([64],block_name+"_mb2")
mwn=get_weight_variable([3,3,96,64],block_name+"_mWnweights")
module1_out=conv2d(x,mw1,strides=[1,2,2,1])
module2_out=conv2d(x,mw21)
module2_out=conv2d(module2_out,mw22,strides=[1,2,2,1])
module2_out+=mb2
module3_out=pooling2d(x,[1,5,5,1],[1,2,2,1])
module_out=module1_out+module2_out
module_out=tf.concat([module_out,module3_out],3)
return conv2d(module_out,mwn)
#這裏所有的 conv 均 padding SAME
block 1 和block 2 區別在於 1:filter(卷積核)個數 一個32 一個64,block2 卷積和 pooling 均 用了stride 2來縮小 feature map(一下簡稱 fm)的 W 和H
block結構如下:
爲什麼 branch1 與branch2 用add? 爲了減少參數量....
爲什麼,branch2 加了bias branch1 沒有? 因爲我branch1 忘加了...
有了block 就可以搭模型了!
模型結構如下:
x=tf.placeholder(shape=[None,784],dtype=tf.float32)
y=tf.placeholder(shape=[None,10],dtype=tf.float32)
x_image=tf.reshape(x,[-1,28,28,1])
keep=tf.placeholder(tf.float32)
block1_out=block1(x_image,"block1")
block1_out=tf.nn.swish(block1_out,name="block_1_act")
block1_out=tf.nn.dropout(block1_out,keep)
block2_out=block2(block1_out,"block2")
block2_out=pooling2d(block2_out,[1,3,3,1],[1,2,2,1])
block2_out=tf.nn.swish(block2_out,name="block_2_act")
block2_out=tf.nn.dropout(block2_out,keep)
gcw=get_weight_variable([7,7,64,1024],name="globalconv")
block_out=tf.nn.conv2d(block2_out,gcw,padding="VALID",strides=[1,1,1,1])
block_out=tf.reshape(block_out,[-1,1024])
#softmax
softmax_w=get_weight_variable([1024,10],"softmax_w")
softmax_b=get_bias_variable([10],"softmax_b")
y_out=tf.nn.softmax(tf.matmul(block_out,softmax_w)+softmax_b)
使用交叉熵作爲 loss
使用 adam lr=1e-4
模型結構大概長這樣:
測試集上平均結果在98.5左右
改進1. 在輸入時增加 規範化層即 (x-mean)/variance,同時再增加一個3*3的conv增大一些 chennels 再進block
在最後一個7*7的conv後增加activation function
規範化層:
#change 1:normalize input
mean,var=tf.nn.moments(x_image,[1,2],keep_dims=True)
x_image=tf.subtract(x_image,mean)
x_image=tf.divide(x_image,var)
最終 acc 98.91左右,已經非常接近了!
改進2:把最後的conv7*7 和FC用 global average pool +conv1*1 替換
#change3 add glob avg pool -- bad
# block_out=tf.nn.avg_pool(block2_out,[1,7,7,1],[1,1,1,1],padding="VALID")
# gcw=get_weight_variable([1,1,64,10],name="globalconv")
# block_out=tf.nn.conv2d(block_out,gcw,padding="VALID",strides=[1,1,1,1])
很差,acc 只有0.8幾,應該是fm chennel太少導致特徵不夠
所以我改成了:
gcw1=get_weight_variable([7,7,64,1024],name="globalconv1")
block_out=tf.nn.conv2d(block2_out,gcw1,padding="VALID",strides=[1,1,1,1])
#activate function nuibi
block_out=tf.nn.swish(block_out)
gcw2=get_weight_variable([1,1,1024,10],name="globalconv2")
block_out=tf.nn.conv2d(block_out,gcw2,padding="VALID",strides=[1,1,1,1])
conv7*7 加conv1*1
用 conv 1*1 替換 之前的FC
感覺提升不明顯(但是換種角度想,參數量比fc下降了呀)
最差結果在98.6 最好的結果99.2 已經超越kaggle了!
之後在訓練策略上改變了下,前800 step batchsize=128 後 200 step batchsize 喪心病狂到512
前900 step 還用 adam 後100步用 sgd lr=1e-14 微調
最終結果....
訓練了幾次結果比較穩定在 99.05左右,emmmm 勉強算是超越了,之後準備在模型結構上改下試試。
代碼在:
https://github.com/lordrebel/beatLenet5/tree/master model1.py