python mobile,MobileNet原理+手写python代码实现MobileNet

最近看到一个巨牛的人工智能教程,分享一下给大家。教程不仅是零基础,通俗易懂,而且非常风趣幽默,像看小说一样!觉得太牛了,所以分享给大家。平时碎片时间可以当小说看,【点这里可以去膜拜一下大神的“小说”】。

MobileNet是针对移动端优化的卷积,所以当需要压缩模型时,可以考虑使用MobileNet替换卷积。下面我们开始学习MobileNet原理,并且先通过Tensorflow函数接口实现MobileNet,再手写python代码实现MobileNet。

1 对比普通卷积和MobileNet原理

MobileNet是用于替换普通卷积,相比普通卷积,MobileNet参数更少,计算速度更快。我们先看一下输入为(h=12,w=12,c=4),卷积为3*3,输出为(h=12,w=12,c=2)前向计算中,普通卷积的参数量、乘法计算次数。普通卷积如下图所示:

7bef96816f7d

普通卷积

从上图可以很简单的计算到,普通卷积参数总数为72个,需要做10368次乘法计算。

相比普通卷积,MobileNet采用的方法是,将卷积分解为2个操作:depthwise和pointwise。pointwise比较容易理解,就是普通的卷积核为11的卷积。depthwise采用的方法不是普通卷积方式,我们知道,对于输入通道数为4的feature map在计算卷积时,输出的每个通道都需要对应4个33卷积核参数。这一步是最主要的耗时,为了提升计算速度,MobileNet把每个输入feature map对应一个33卷积核,输出通道数不变,即为4。而真正对通道数做改变的是在pointwise,也就是11的卷积。

注意:上面面论述针对的是输入为(h=12,w=12,c=4),卷积为3*3,输出为(h=12,w=12,c=2) 这种情况举例说明。

下面图很清晰的理解mobilenet原理:

7bef96816f7d

mobilenet

从上图可以很简单的计算到,普通卷积参数总数为44个,需要做6336次乘法计算。可以看到,mobilenet的参数和乘法计算次数明显比普通卷积要小。这还仅仅是我列举的简单例子,在实际网络中,几十层的网络很常见,feature map也是远远大于12124。根据我的经验,普通100M的网络模型,将所有卷积替换成mobilenet后,能降到20M以下,计算速度更是不在一个量级。

2 Tensorflow中使用MobileNet

在Tensorflow中,有depthwise对应的函数接口,直接调用就可以了。由于pointwise就是普通的卷积核大小为1*1的卷积,而卷积的原理,我们在《Tensorflow卷积实现原理+手写python代码实现卷积》一文中已经讲的很清楚了。所以我们只要关注depthwise即可。

在Tensorflow中,depthwise操作接口是:

tf.nn.depthwise_conv2d(

input,

filter,

strides,

padding,

rate=None,

name=None,

data_format=None

)

假设我们的输入和卷积核如下:

#输入,shape=[c,h,w]=[2,5,5]

input_data=[

[[1,0,1,2,1],

[0,2,1,0,1],

[1,1,0,2,0],

[2,2,1,1,0],

[2,0,1,2,0]],

[[2,0,2,1,1],

[0,1,0,0,2],

[1,0,0,2,1],

[1,1,2,1,0],

[1,0,1,1,1]],

]

#卷积核,shape=[in_c,k,k]=[2,3,3]

weights_data=[

[[ 1, 0, 1],

[-1, 1, 0],

[ 0,-1, 0]],

[[-1, 0, 1],

[ 0, 0, 1],

[ 1, 1, 1]]

]

下面我们贴上完整调用depthwise的代码:

import tensorflow as tf

def get_shape(tensor):

[s1,s2,s3]= tensor.get_shape()

s1=int(s1)

s2=int(s2)

s3=int(s3)

return s1,s2,s3

def chw2hwc(chw_tensor):

[c,h,w]=get_shape(chw_tensor)

cols=[]

for i in range(c):

#每个通道里面的二维数组转为[w*h,1]即1列

line = tf.reshape(chw_tensor[i],[h*w,1])

cols.append(line)

#横向连接,即将所有竖直数组横向排列连接

input = tf.concat(cols,1)#[w*h,c]

#[w*h,c]-->[h,w,c]

input = tf.reshape(input,[h,w,c])

return input

def hwc2chw(hwc_tensor):

[h,w,c]=get_shape(hwc_tensor)

cs=[]

for i in range(c):

#[h,w]-->[1,h,w]

channel=tf.expand_dims(hwc_tensor[:,:,i],0)

cs.append(channel)

#[1,h,w]...[1,h,w]---->[c,h,w]

input = tf.concat(cs,0)#[c,h,w]

return input

def tf_depthwise(input,weights ):

depthwise=tf.nn.depthwise_conv2d( input, weights, [1, 1, 1, 1], padding='SAME' )

return depthwise

def main():

const_input = tf.constant(input_data , tf.float32)

const_weights = tf.constant(weights_data , tf.float32 )

input = tf.Variable(const_input,name="input")

#[2,5,5]------>[5,5,2]

input=chw2hwc(input)

#[5,5,2]------>[1,5,5,2]

input=tf.expand_dims(input,0)

weights = tf.Variable(const_weights,name="weights")

#[2,3,3]-->[3,3,2]

weights=chw2hwc(weights)

#[3,3,2]-->[3,3,2,1]

weights=tf.expand_dims(weights,3)

print(weights.get_shape().as_list())

#[b,h,w,c]

conv=tf_depthwise(input,weights )

rs=hwc2chw(conv[0])

init=tf.global_variables_initializer()

sess=tf.Session()

sess.run(init)

conv_val = sess.run(rs)

print(conv_val)

if __name__=='__main__':

main()

打印结果如下:

[[[ 1. -3. 0. 1. -2.]

[-1. 3. 1. -1. 3.]

[ 1. -1. 0. 3. -2.]

[ 1. 1. 1. -2. 1.]

[ 4. 1. 4. 2. -1.]]

[[ 1. 3. 2. 3. 2.]

[ 2. 1. 3. 4. 2.]

[ 3. 4. 5. 6. 1.]

[ 2. 3. 5. 4. 0.]

[ 1. 2. 1. -1. -1.]]]

我们通过一个动画演示计算过程:

[图片上传失败...(image-f2e073-1530334796526)]

3 手写python代码实现depthwise

import numpy as np

input_data=[

[[1,0,1,2,1],

[0,2,1,0,1],

[1,1,0,2,0],

[2,2,1,1,0],

[2,0,1,2,0]],

[[2,0,2,1,1],

[0,1,0,0,2],

[1,0,0,2,1],

[1,1,2,1,0],

[1,0,1,1,1]]

]

weights_data=[

[[ 1, 0, 1],

[-1, 1, 0],

[ 0,-1, 0]],

[[-1, 0, 1],

[ 0, 0, 1],

[ 1, 1, 1]]

]

#fm:[h,w]

#kernel:[k,k]

#return rs:[h,w]

def compute_conv(fm,kernel):

[h,w]=fm.shape

[k,_]=kernel.shape

r=int(k/2)

#定义边界填充0后的map

padding_fm=np.zeros([h+2,w+2],np.float32)

#保存计算结果

rs=np.zeros([h,w],np.float32)

#将输入在指定该区域赋值,即除了4个边界后,剩下的区域

padding_fm[1:h+1,1:w+1]=fm

#对每个点为中心的区域遍历

for i in range(1,h+1):

for j in range(1,w+1):

#取出当前点为中心的k*k区域

roi=padding_fm[i-r:i+r+1,j-r:j+r+1]

#计算当前点的卷积,对k*k个点点乘后求和

rs[i-1][j-1]=np.sum(roi*kernel)

return rs

def my_depthwise(chw_input,chw_weights):

[c,_,_]=chw_input.shape

[_,k,_]=chw_weights.shape

#outputs=np.zeros([h,w],np.float32)

outputs=[] #注意跟conv的区别

#对每个feature map遍历,从而对每个feature map进行卷积

for i in range(c):

#feature map==>[h,w]

f_map=chw_input[i]

#kernel ==>[k,k]

w=chw_weights[i]

rs =compute_conv(f_map,w)

#outputs=outputs+rs

outputs.append(rs) #注意跟conv的区别

return np.array( outputs)

def main():

#shape=[c,h,w]

input = np.asarray(input_data,np.float32)

#shape=[in_c,k,k]

weights = np.asarray(weights_data,np.float32)

rs=my_depthwise(input,weights)

print(rs)

if __name__=='__main__':

main()

同样,注释写的很清楚,不再解释代码。运行结果如下:

[[[ 1. -3. 0. 1. -2.]

[-1. 3. 1. -1. 3.]

[ 1. -1. 0. 3. -2.]

[ 1. 1. 1. -2. 1.]

[ 4. 1. 4. 2. -1.]]

[[ 1. 3. 2. 3. 2.]

[ 2. 1. 3. 4. 2.]

[ 3. 4. 5. 6. 1.]

[ 2. 3. 5. 4. 0.]

[ 1. 2. 1. -1. -1.]]]

可以看到,跟tensorflow的结果是一模一样。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值