『TensorFlow』读书笔记_ResNet_V2

对比之前的复杂版本,这次的torch实现其实简单了不少,不过这和上面的代码实现逻辑过于复杂也有关系。

一、PyTorch实现

# Author : hellcat
# Time : 18-3-2 """
import os
os.environ["CUDA_VISIBLE_DEVICES"]="-1" import numpy as np
np.set_printoptions(threshold=np.inf) import tensorflow as tf
config = tf.ConfigProto()
config.gpu_options.allow_growth = True
sess = tf.Session(config=config)
""" import torch as t
import torch.nn as nn
from torch.nn import functional as F class ResidualBlock(nn.Module):
def __init__(self, inchannel, outchannel, stride=1, shortcut=None):
super(ResidualBlock, self).__init__()
self.left = nn.Sequential(
nn.Conv2d(inchannel, outchannel, kernel_size=3, stride=stride, padding=1),
nn.BatchNorm2d(outchannel),
nn.ReLU(inplace=True),
nn.Conv2d(outchannel, outchannel, 3, 1, 1, bias=False),
nn.BatchNorm2d(outchannel)
)
self.right = shortcut def forward(self, x):
out = self.left(x)
residual = x if self.right is None else self.right(x)
out += residual
return F.relu(out) class ResNet(nn.Module):
def __init__(self, num_classes=1000):
super(ResNet, self).__init__()
self.pre = nn.Sequential(
nn.Conv2d(3, 64, 7, 2, 3, bias=False),
nn.BatchNorm2d(64),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
)
self.layer1 = self._make_layer(inchannel=64, outchannel=128, block_num=3)
self.layer2 = self._make_layer(inchannel=128, outchannel=256, block_num=4, stride=2)
self.layer3 = self._make_layer(inchannel=256, outchannel=512, block_num=6, stride=2)
self.layer4 = self._make_layer(inchannel=512, outchannel=512, block_num=3, stride=2) self.fc = nn.Linear(512, num_classes) def _make_layer(self, inchannel, outchannel, block_num, stride=1):
shortcut = nn.Sequential(
nn.Conv2d(inchannel, outchannel, 1, stride, bias=False),
nn.BatchNorm2d(outchannel)
)
layers = []
layers.append(ResidualBlock(inchannel, outchannel, stride, shortcut))
for i in range(1, block_num):
layers.append(ResidualBlock(outchannel, outchannel))
return nn.Sequential(*layers) def forward(self, x):
x = self.pre(x) # [1, 64, 56, 56]
x = self.layer1(x) # [1, 128, 56, 56]
x = self.layer2(x) # [1, 256, 28, 28]
x = self.layer3(x) # [1, 512, 14, 14]
x = self.layer4(x) # [1, 512, 7, 7] x = F.avg_pool2d(x, 7)
x = x.view(x.size(0), -1)
return self.fc(x) def hook(module, inputdata, output):
'''把这层的输出拷贝到features中'''
print("钩子输出:", output.data.size()) module = ResNet()
img = t.autograd.Variable(t.randn(1, 3, 224, 224))
handle = module.pre[0].register_forward_hook(hook)
out = module(img)
handle.remove()
print(out)

上面代码中,我们注册了钩子尝试分析一下中间的输出,可以看到,torch中的卷积层默认是SAME模式,输出就是in/stride,和TensorFlow一致,

torch.Size([1, 64, 112, 112])
Variable containing:
 0.6336 -0.5863  0.6472  ...  -0.4694  0.1808  0.2837
[torch.FloatTensor of size 1x1000]

二、TensorFlow实现

同样的逻辑下,ResNet34的TensorFlow实现如下,使用的封装包ops,之前有介绍过,这里面小修了卷积层的封装,使得conv2d可以舍弃bias(就是卷及计算后不加偏执),

# Author : hellcat
# Time : 18-3-7 """
import os
os.environ["CUDA_VISIBLE_DEVICES"]="-1" import numpy as np
np.set_printoptions(threshold=np.inf)
"""
import ops
import tensorflow as tf
config = tf.ConfigProto()
config.gpu_options.allow_growth = True
sess = tf.Session(config=config) def ResidualBlock(x,
outchannel, stride=1, shortcut=None,
train=True, name="ResidualBlock"):
with tf.variable_scope(name):
conv1 = ops.conv2d(x, outchannel,
k_h=3, k_w=3,
s_h=stride, s_w=stride, scope="conv1")
bn1 = tf.nn.relu(ops.batch_normal(conv1, train=train, scope="bn1"))
conv2 = ops.conv2d(bn1, outchannel,
k_h=3, k_w=3,
s_h=1, s_w=1,
with_bias=False, scope="conv2")
left = ops.batch_normal(conv2, train=train, scope="bn2")
right = x if shortcut is None else shortcut(x)
return tf.nn.relu(left + right) class ResNet():
def __init__(self):
with tf.variable_scope("input"):
x = tf.placeholder(dtype=tf.float32, shape=[1, 224, 224, 3])
with tf.variable_scope("pre"):
conv = ops.conv2d(x, output_dim=64,
k_h=7, k_w=7,
s_h=2, s_w=2,
with_bias=False)
bn = tf.nn.relu(ops.batch_normal(conv))
pool = tf.nn.max_pool(bn, ksize=[1, 3, 3, 1],
strides=[1, 2, 2, 1], padding='SAME') with tf.variable_scope("layer1"):
layer1 = self._make_layer(pool, outchannel=128, block_num=3)
with tf.variable_scope("layer2"):
layer2 = self._make_layer(layer1, outchannel=256, block_num=4, stride=2)
with tf.variable_scope("layer3"):
layer3 = self._make_layer(layer2, outchannel=512, block_num=6, stride=2)
with tf.variable_scope("layer4"):
layer4 = self._make_layer(layer3, outchannel=512, block_num=3, stride=2)
# Tensor("layer1/ResidualBlock2/Relu_1:0", shape=(1, 56, 56, 128), dtype=float32)
# Tensor("layer2/ResidualBlock3/Relu_1:0", shape=(1, 28, 28, 256), dtype=float32)
# Tensor("layer3/ResidualBlock5/Relu_1:0", shape=(1, 14, 14, 512), dtype=float32)
# Tensor("layer4/ResidualBlock2/Relu_1:0", shape=(1, 7, 7, 512), dtype=float32)
pool = tf.nn.avg_pool(layer4, ksize=[1, 7, 7, 1],
strides=[1, 7, 7, 1], padding='SAME')
reshape = tf.reshape(pool, [layer4.get_shape()[0], -1])
self.fc = ops.linear(reshape, 1000) def __call__(self, *args, **kwargs):
return self.fc def _make_layer(self,x,
outchannel,
block_num, stride=1): def shortcut(input_):
with tf.variable_scope("shortcut"):
conv = ops.conv2d(input_, output_dim=outchannel,
k_w=1, k_h=1, s_w=stride, s_h=stride,
with_bias=False)
return ops.batch_normal(conv) x = ResidualBlock(x, outchannel, stride,
shortcut, name="ResidualBlock0")
for i in range(1, block_num):
x = ResidualBlock(x, outchannel,
name="ResidualBlock{}".format(i))
return x if __name__ == "__main__":
resnet = ResNet()
print(resnet())

ops.py卷积修改封装如下,

def conv2d(input_, output_dim,
k_h=5, k_w=5, s_h=2, s_w=2, stddev=0.02,
scope="conv2d", with_w=False, with_bias=True):
"""
卷积网络封装
:param input_:
:param output_dim: 输出的feature数目
:param k_h:
:param k_w:
:param s_h:
:param s_w:
:param stddev:
:param scope:
:param with_w:
:param with_bias: 是否含有bias层
:return:
""" with tf.variable_scope(scope):
w = tf.get_variable('w', [k_h, k_w, input_.get_shape()[-1], output_dim],
initializer=tf.truncated_normal_initializer(stddev=stddev))
conv = tf.nn.conv2d(input_, w, strides=[1, s_h, s_w, 1], padding='SAME')
if with_bias:
biases = tf.get_variable('biases', [output_dim], initializer=tf.constant_initializer(0.0))
conv = tf.reshape(tf.nn.bias_add(conv, biases), conv.get_shape())
else:
biases = None if with_w:
return conv, w, biases
else:
return conv

输出如下,

Tensor("Linear/add:0", shape=(1, 1000), dtype=float32)

附:

ops.py截止本文发布的最新版本状态,

# Author : hellcat
# Time : 18-1-21
# Usage : 网络层函数封装
"""
conv2d
deconv2d
lrelu
linear
""" import tensorflow as tf # def batch_normal(x, train=True, epsilon=1e-5, decay=0.9, scope="batch_norm"):
# return tf.contrib.layers.batch_norm(x,
# decay=decay,
# updates_collections=None,
# epsilon=epsilon,
# scale=True,
# is_training=train,
# scope=scope) def batch_normal(x, epsilon=1e-5, momentum=0.9, train=True, scope='batch_norm'):
with tf.variable_scope(scope):
return tf.contrib.layers.batch_norm(x,
decay=momentum,
updates_collections=None,
epsilon=epsilon,
scale=True,
is_training=train)
'''
Note: when training, the moving_mean and moving_variance need to be updated.
By default the update ops are placed in `tf.GraphKeys.UPDATE_OPS`, so they
need to be added as a dependency to the `train_op`. For example: ```python
update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
with tf.control_dependencies(update_ops):
train_op = optimizer.minimize(loss)
``` One can set updates_collections=None to force the updates in place, but that
can have a speed penalty, especially in distributed settings.
''' # class batch_norm(object):
# def __init__(self, epsilon=1e-5, decay=0.9, scope="batch_norm"):
# with tf.variable_scope(scope):
# self.epsilon = epsilon
# self.decay = decay
# # self.scope = scope
#
# def __call__(self, x, scope, train=True):
# return tf.contrib.layers.batch_norm(x,
# decay=self.decay,
# updates_collections=None,
# epsilon=self.epsilon,
# scale=True,
# is_training=train,
# scope=scope) def concat(tensor_a, tensor_b):
"""
组合Tensor,注意的是这里tensor_a的宽高应该大于等于tensor_b
:param tensor_a: 前面的tensor
:param tensor_b: 后面的tensor
:return:
"""
if tensor_a.get_shape().as_list()[1] > tensor_b.get_shape().as_list()[1]:
return tf.concat([tf.slice(tensor_a,
begin=[0, (int(tensor_a.shape[1]) - int(tensor_b.shape[1])) // 2,
(int(tensor_a.shape[1]) - int(tensor_b.shape[1])) // 2, 0],
size=[int(tensor_b.shape[0]), int(tensor_b.shape[1]),
int(tensor_b.shape[2]), int(tensor_a.shape[3])],
name='slice'),
tensor_b],
axis=3, name='concat') elif tensor_a.get_shape().as_list()[1] < tensor_b.get_shape().as_list()[1]:
return tf.concat([tensor_a,
tf.slice(tensor_b,
begin=[0, (int(tensor_b.shape[1]) - int(tensor_a.shape[1])) // 2,
(int(tensor_b.shape[1]) - int(tensor_a.shape[1])) // 2, 0],
size=[int(tensor_a.shape[0]), int(tensor_a.shape[1]),
int(tensor_a.shape[2]), int(tensor_b.shape[3])],
name='slice')],
axis=3, name='concat')
else:
return tf.concat([tensor_a, tensor_b], axis=3) def conv_cond_concat(x, y):
"""
广播并连接向量,用于ac_gan的标签对矩阵拼接
:param x: features,例如shape:[n,16,16,128]
:param y: 扩暂维度后的标签,例如shape:[n,1,1,10]
:return: 拼接后features,例如:[n,16,16,138]
"""
x_shapes = x.get_shape()
y_shapes = y.get_shape()
return tf.concat([x, y * tf.ones([x_shapes[0], x_shapes[1], x_shapes[2], y_shapes[3]])], axis=3) def conv2d(input_, output_dim,
k_h=5, k_w=5, s_h=2, s_w=2, stddev=0.02,
scope="conv2d", with_w=False, with_bias=True):
"""
卷积网络封装
:param input_:
:param output_dim: 输出的feature数目
:param k_h:
:param k_w:
:param s_h:
:param s_w:
:param stddev:
:param scope:
:param with_w:
:param with_bias: 是否含有bias层
:return:
""" with tf.variable_scope(scope):
w = tf.get_variable('w', [k_h, k_w, input_.get_shape()[-1], output_dim],
initializer=tf.truncated_normal_initializer(stddev=stddev))
conv = tf.nn.conv2d(input_, w, strides=[1, s_h, s_w, 1], padding='SAME')
if with_bias:
biases = tf.get_variable('biases', [output_dim], initializer=tf.constant_initializer(0.0))
conv = tf.reshape(tf.nn.bias_add(conv, biases), conv.get_shape())
else:
biases = None if with_w:
return conv, w, biases
else:
return conv def deconv2d(input_, output_shape,
k_h=5, k_w=5, s_h=2, s_w=2, stddev=0.02,
scope="deconv2d", with_w=False):
"""
转置卷积网络封装
:param input_:
:param output_shape: 输出的shape
:param k_h:
:param k_w:
:param s_h:
:param s_w:
:param stddev:
:param scope:
:param with_w:
:return:
"""
with tf.variable_scope(scope):
# filter : [height, width, output_channels, in_channels]
w = tf.get_variable('w', [k_h, k_w, output_shape[-1], input_.get_shape()[-1]],
initializer=tf.random_normal_initializer(stddev=stddev)) try:
deconv = tf.nn.conv2d_transpose(input_, w, output_shape=output_shape,
strides=[1, s_h, s_w, 1]) # Support for verisons of TensorFlow before 0.7.0
except AttributeError:
deconv = tf.nn.deconv2d(input_, w, output_shape=output_shape,
strides=[1, s_h, s_w, 1]) biases = tf.get_variable('biases', [output_shape[-1]], initializer=tf.constant_initializer(0.0))
deconv = tf.reshape(tf.nn.bias_add(deconv, biases), deconv.get_shape()) if with_w:
return deconv, w, biases
else:
return deconv def lrelu(x, leak=0.2):
"""
Leak_Relu层封装
:param x:
:param leak:
:return:
"""
return tf.maximum(x, leak*x) def linear(input_, output_size,
stddev=0.02, bias_start=0.0,
scope=None, with_w=False):
"""
全连接层封装
:param input_:
:param output_size: 输出节点数目
:param scope:
:param stddev:
:param bias_start: 使用常数初始化偏执,常数值设定
:param with_w: 返回是否返回参数Variable
:return:
"""
shape = input_.get_shape().as_list() with tf.variable_scope(scope or "Linear"):
matrix = tf.get_variable("Matrix", [shape[1], output_size], tf.float32,
tf.random_normal_initializer(stddev=stddev))
bias = tf.get_variable("bias", [output_size], initializer=tf.constant_initializer(bias_start)) if with_w:
return tf.matmul(input_, matrix) + bias, matrix, bias
else:
return tf.matmul(input_, matrix) + bias

『PyTorch × TensorFlow』第十七弹_ResNet快速实现的更多相关文章

  1. 『TensorFlow』第七弹_保存&载入会话_霸王回马

    首更: 由于TensorFlow的奇怪形式,所以载入保存的是sess,把会话中当前激活的变量保存下来,所以必须保证(其他网络也要求这个)保存网络和载入网络的结构一致,且变量名称必须一致,这是caffe ...

  2. 关于『进击的Markdown』:第五弹

    关于『进击的Markdown』:第五弹 建议缩放90%食用 路漫漫其修远兮,吾将上下而求索.  我们要接受Mermaid的考验了呢  Markdown 语法真香(一如既往地安利) ( 进击吧!Mark ...

  3. 关于『进击的Markdown』:第四弹

    关于『进击的Markdown』:第四弹 建议缩放90%食用 美人鱼(Mermaid)悄悄的来,又悄悄的走,挥一挥匕首,不留一个活口 又是漫漫画图路... 女士们先生们,大家好!  我们要接受Markd ...

  4. 关于『进击的Markdown』:第三弹

    关于『进击的Markdown』:第三弹 建议缩放90%食用 我与神明画押,赌这弹markdown又双叒叕拖稿了 %%%Markdown!我的CSDN编辑器崩了呜呜呜 各路英雄豪杰,大家好!  我们要开 ...

  5. 『PyTorch x TensorFlow』第六弹_从最小二乘法看自动求导

    TensoFlow自动求导机制 『TensorFlow』第二弹_线性拟合&神经网络拟合_恰是故人归 下面做了三个简单尝试, 利用包含gradients.assign等tf函数直接构建图进行自动 ...

  6. 『PyTorch x TensorFlow』第八弹_基本nn.Module层函数

    『TensorFlow』网络操作API_上 『TensorFlow』网络操作API_中 『TensorFlow』网络操作API_下 之前也说过,tf 和 t 的层本质区别就是 tf 的是层函数,调用即 ...

  7. 『TensorFlow』第十一弹_队列&多线程&TFRecod文件_我辈当高歌

    TF数据读取队列机制详解 一.TFR文件多线程队列读写操作 TFRecod文件写入操作 import tensorflow as tf def _int64_feature(value): # val ...

  8. 『TensorFlow』第十弹_队列&多线程_道路多坎坷

    一.基本队列: 队列有两个基本操作,对应在tf中就是enqueue&dequeue tf.FIFOQueue(2,'int32') import tensorflow as tf '''FIF ...

  9. 『TensorFlow』第三弹_可视化框架介绍_悄悄问圣僧

    添加记录节点 -> 汇总记录节点 -> run汇总节点 -> [书写器生成]书写入文件 [-> 刷新缓冲区] 可视化关键点: 注意, 1.with tf.name_scope( ...

随机推荐

  1. MVEAN_day05 Nexus私服对的搭建

    序言:为什么要搭建私服 因为在公司中我们是以团队进行开发的,不在是在自己的本地仓库中进行,我们需要连接公司远程仓库进行jar依赖.在公司的局域网中搭建的私服,然后开发人员连接这台计算机,进行团队开发. ...

  2. 剑指offer-复杂链表的复制

    题目描述 输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head.(注意,输出结果中请不要返回参数中的节点引用,否 ...

  3. python类型错误:'NoneType' object is not subscriptable

    TypeError: 'NoneType' object is not subscriptable --> 原因:变量使用了系统内置的关键字list 解决:重新定义下这个变量

  4. [macOS] Error: /usr/local must be writable!" (Sierra 10.12 )

    Error: /usr/local must be writable!" (Sierra 10.12 ) solution: sudo chown -R $(whoami) /usr/loc ...

  5. Lua C/C++互相调用

    先来说下大致脚本引擎框架,此次采用如下,即运行C++代码启动程序,然后加载Lua脚本执行! 1.基础 Lua脚本中只能调用 int (*lua_CFunction) (lua_State *L) 这种 ...

  6. 大堆文字不如几张图片-论信息传递的方式以NodeMCU入门为例

  7. JavaWeb-----实现第一个Servlet程序

    1.Servlet简介      Servlet是在服务器端运行的一个小程序,实际上一个Servlet就是一个Java类,并且可以通过“请求-响应”编程模型来访问的这个驻留在服务器内 存里的servl ...

  8. Flask最强攻略 - 跟DragonFire学Flask - 第十五篇 Flask-Script

    其实本章就是为下一章做的铺垫啦,但是也要认真学习哦 Flask-Script 从字面意思上来看就是 Flask 的脚本 是的,熟悉Django的同学是否还记得Django的启动命令呢? python ...

  9. DelayQueue源码解析

    DelayQueue是一个支持延时获取元素的无界阻塞队列.里面的元素全部都是“可延期”的元素,列头的元素是最先“到期”的元素,如果队列里面没有元素到期,是不能从列头获取元素的,哪怕有元素也不行.也就是 ...

  10. 基于框架的RPC通信技术原理解析

    RPC的由来 随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进. 单一应用架构 当网站流量很小时, ...