tf中矩阵量存在形式常用有三种,具体如下:
1.tf.Variable()
表示神经网络中可变化的量(可以通过trainable=False设置成不可变),可在运行中赋值,可以通过constant或者其他方式进行初始化。
2.tf.constant()
可以通过numpy中的array或者list,还有给定的shape和数值进行赋值
3.tf.placeholder()
相当于占位符,也是有shape的量,因为训练过程中需要不断赋值和替换值,而整体计算的结构是不变的。
代码:
//导包
import tensorflow as tf
//定义变量A = tf.Variable(tf.ones([4,4]))
//变量初始化import numpy as np
cst = tf.constant(np.ones([4,4]),dtype=tf.float32)
需要指定类型dtype=tf.float32,tf中不能隐式转换浮点和整型
cst = tf.constant(1.0,shape=[4,4],dtype=tf.float32)也是可以的
A = tf.Variable(cst)
//定义placeholderX = tf.placeholder(dtype=tf.float32,shape=[4,1])
//矩阵相乘C = tf.matmul(A,X)
//定义Sessionsess = tf.Session()
//初始化变量init = tf.global_variables_initializer()
//执行初始化sess.run(init)
//运行矩阵相乘sess.run(C,feed_dict={X:[[1],[1],[1],[1]]})
//获取变量值A_val = A.value()
Avalue = sess.run(A_val)
//完整矩形相乘代码import tensorflow as tf
import numpy as np
X = tf.placeholder(dtype=tf.float32,shape=[4,1])
A = tf.Variable(tf.zeros([4,4]))
C = tf.matmul(A,X)
sess = tf.session()
init = tf.global_variables_initializer()
sess.run(init)
print(sess.run(A))
//为了使计算图更加清晰,可以使用variable_scope()
//定义变量名称with tf.variable_scope("first-nn-layer"):
W = tf.Variable(tf.zeros([784,10]),name="W")
b = tf.Variable(tf.zeros([10]),name="b")
y = tf.matmul(x,W)+b
variable_summaries(W)
//标识不同的变量
//不同作用域下的同名变量with tf.variable_scope("first-nn-layer"):
W = tf.Variable(tf.zeros([784,10]),name="W")
b = tf.Variable(tf.zeros([10]),name="b")
W1 = tf.Variable(tf.zeros([784,10]),name="W")
print(W.name)
print(W1.name)
w、w1虽然name一样,但是计算中依然当成不同的变量,让同一个scope的同一变量可以通过get_variable()函数
//获取变量with tf.variable_scope("first-nn-layer") as scope:
W = tf.get_variable("W",[784, 10])
b = tf.get_variable("b",[10])
scope.reuse_variables()#缺少则会报错
W1 = tf.get_variables("W",shape=[784,10])
print(W.name)
print(W1.name)
w、w1属于同一个变量
注:若此时缺少了scope.reuse_variables()则会报错,因为同时引用了同一个变量,对于不同层的变量,可以利用variable_scope进行区分,在再次引入相关变量时,需要加上reuse=True,否则依然会报错。如果变量不存在时加上reuse=True,依然会报错,因为该变量不存在
with tf.variable_scope("first-nn-layer") as scope:
W = tf.get_variable("W",[784, 10])
b = tf.get_variable("b",[10])
with tf.variable_scope("second-nn-layer") as scope:
W = tf.get_variable("W",[784, 10])
b = tf.get_variable("b",[10])
with tf.variable_scope("second-nn-layer", reuse=True) as scope:
W3 = tf.get_variable("W",[784, 10])
b3 = tf.get_variable("b",[10])
print(W.name)
print(W3.name)
//保存模型
//定义saversaver = tf.train.Saver()
//在训练过程中进行保存,保存为训练过程中的变量
//变量保存for itr in range(1000):
...
saver.save(sess,"model/al",global_step=itr)
//加载计算
//变量载入saver.restore(sess,"model/v2-200")
3.4构建计算图
//前面在描述计算图,这里观察所建立的计算图
//定义summarytrain_writer = tf.summary.FileWriter("logdir",sess.graph)
注:sess.graph就是描绘的计算图,”logdir”是log的存储文件夹。在Shell中运行Tensorboard,在浏览器中输入localhost:6006,然后点击graph就可以看到设计的网络模型了。
//问题:描绘的计算图非常杂乱无章,变量命名的可读性很差,需要进行整理。
//变量命名x = tf.placeholder(tf.float32,[None,784],name="input_x")
label = tf.placeholder(tf.float32,[None,10],name="input_label")
W = tf.Variable(tf.zeros([874,10]),name="W")
b = tf.Variable(tf.zeros([10]),name="b")
//问题:依然不够清楚,可以将输入层的x和label归为一类
//定义作用域with tf.variable_scope("input"):
x = tf.placeholder(tf.float32,[None,784],name="input_x")
label = tf.placeholder(tf.float32,[None,10],name="input_label")
with tf.variable_scope("first-nn-layer"):
W = tf.Variable(tf.zeros([784,10]), name="W")
b = tf.Variable(tf.zeros([10]),name="b")
y = tf.matmul(x,W)+b
with tf.variable_scope("loss"):
loss = tf.reduce_mean(tf.square(y-label))
//同一作用域下的同名变量是相同的,涉及到变量复用的问题,以及后续变量的获取,为了观察变量的变化,需要观察的变量加入summary函数
//定义summary函数def variable_summaries(var):
with tf.name_scope('summaries'):
mean = tf.reduce_mean(var)
tf.summary.scalar('mean',mean)
with tf.name_scope('stddev'):
stddev = tf.sqrt(tf.reduce_mean(tf.square(var-mean)))
tf.summary.scalar('stddev',stddev)
tf.summary.scalar('max',tf.reduce_max(var))
tf.summary.scalar('min',tf.reduce_min(var))
tf.summary.histogram('histogram',var)
//若要观测W的相关情况,调用summary函数
//调用summary函数variable_summaries(W)
//再用merge_all函数收集summary信息
//获取summary信息merged = tf.summary.merge_all()
//summary保存summary = sess.run(merged, feed_dict={x:batch_xs,label:batch_ys})
train_writer.add_summary(summary,itr)
注:此时可以在网页中访问,观察变量随迭代变化的情况,可以通过不同的方式对变量进行观测,比如时序统计、histogram,这些统计信息对于分析训练过程是非常重要的
3.5全连接网络构建
//tf官方手写识别版本的简化版本
//单层全连接网络
引入库
from tensorflow.examples.tutorials.mnist import input_data#产生数据,手写识别的图片和标签
import tensorflow as tf
获取数据
mnist = input_data.read_data_sets("MNIST_data/",one_hot=True)
构建网络模型
x,label分别为图形数据和标签数据
x = tf.placeholder(tf.float32,[None,784])
label = tf.placeholder(tf.float32,[None,10])
构建单层网络中的权值和偏置
W = tf.Variable(tf.zeros([784,10]))
b = tf.Variable(tf.zeros([10])
本例中无非线性激活函数
y = tf.matmul(x,W)+b
定义损失函数为欧氏距离,但这并不是最好的,多分类问题通常使用交叉熵
loss = tf.reduce_mean(tf.square(y-label))
若使用交叉熵损失函数
soft_max = tf.nn.softmax(logit, axis=1)
loss = tf.reduce_mean(-label*tf.log(soft_max))
用梯度迭代算法
train_step = tf.train.GradientDescentOptimizer(0.005).minimize(loss)
用于验证
correct_prediction = tf.equal(tf.argmax(y,1),tf.argmax(label,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction,tf.float.32))
定义会话
sess = tf.Session()
初始化所有变量
sess.run(tf.global_variable_initializer())
迭代过程
for itr in range(3000):
batch_xs,batch_ys = mnist.train.next_batch(100)
sess.run(train_step, feed_dict={x:batch_xs,label:batch_ys})
if itr%10==0:
print("step:%6d accuracy:"%iter, sess.run(accuracy, feed_dict={x:mnist.test.images, label:mnist.test.labels}))
获取W取值
W_value = sess.run(W.value())
//定义一个单层全连接函数def full_layer(input_tensor, out_dim, name="full"):
with tf.variable_scope(name):
shape = input_tensor.get_shape()as_list()
W = tf.get_variable('W',(shape[1],out_dim),dtype=tf.float32, initalizer=tf.truncated_normal_initializer(stddev=0.1))
b = tf.get_variable('b',[out_dim], dtype=tf.float32, initializer=tf.constant_initializer(0))
out = tf.matmul(input_tensor, W)+b
return tf.nn.sigmoid(nn)
3.6CNN构建
//CNN手写识别
预读取MNIST手写字库
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data",one_hot=True)
import tensorflow as tf
用正态分布随机数初始化变量,本例中仅作为权值
def weight_variable(shape):
initial=tf.truncated_normal(shape,stddev=0.1)
#正态分布
`return tf.Variable(initial)`
用常量方式初始化偏置
def bias_variable(shape):
initial=tf.constant(0.1,shape=shape)
#常数分布
`return tf.Variable(initial)`
定义二维卷积的过程
def conv2d(x,W):
return tf.nn.conv2d(x,W,strides=[1,1,1,1],padding=’SAME’)
定义池化层,简单地说就是选个最大的数,进一步降低自由参数的个数
def max_pool_2x2(x):
return tf.nn.max_pool(x,ksize=[1,2,2,1],strides=[1,2,2,1],padding='SAME')
x = tf.placeholder(tf.float32,shape=[100,784])
y = tf.placeholder(tf.float32,shape=[100,10])
W_conv1 = weight_variable([5,5,1,32])
b_conv1 = bias_variable([32])
x_image = tf.reshape(x,[-1,28,28,1])
y_conv1 = tf.nn.relu(conv2d(x_iamge,W_conv1)+b_conv1)
y_pool1 = max_pool_2x2(y_conv1)
W_conv2 = weight_variable([5,5,32,64])
b_conv2 = weight_variable([64])
y_conv2 = tf.nn.relu(conv2d(y_pool1,W_conv2)+b_conv2)
y_pool2 = max_pool_2x2(y_conv2)
y_fc_flat = tf.reshape(y_pool2,[-1,7*7*64])
W_fc1 = weight_variable([7*7*64,10])
b_fc1 = bias_variable([10])
y_fc1 = tf.nn.relu(tf.matmul(y_fc_flat,W_fc1)+b_fc1)
cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y,logits=y_fc1))
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
sess = tf.Session()
init = tf.global_variables_initializer()
sess.run(init)
for i in range(1000):
bx,by = mnist.train.next_batch(100)
sess.run(train_step,feed_dict={x:bx,y:by})
import numpy as np
import matplotlib.pyplot as plt
设置输出风格,为画图美观
import matplotlib as mpl
mpl.style.use('seaborn-darkgrid')
val = W_conv1.value()
convVal = np.array(sess.run(val))
convVal = np.reshape(convVal,[5,5,32])
plt.imshow(convVal[:,:,6])
plt.show()
3.8多架构运行
//GPU使用
GPU可以加速深度学习作业的训练速度,如果服务器有多个GPU,那么tensorflow默认使用全部
使用部分GPU:
python程序启动时调用:CUDA_VISIBLE_DEVICES=0.2.3 python script.py
python代码内进行调用:import os
os.environ["CUDA_VISIBLE_DEVICES"]="1"
//配置GPU显存
某些情况下,多作业或者共享GPU的场景中,可以控制tf使用GPU显存大小gpuOptions = tf.GPUOptions(per_process_gpu_memory_fraction=0.8)
sess = tf.Session(config=tf.ConfigProto(gpu_options=gpuOptions))
//GPU运行代码
将变量的定义和分配定义到GPU上进行
with tf.device('/gpu:0'):
W = tf.get_variable('W',(in_dim,out_dim),dtype=tf.float32,initializer=tf.truncated_normal_initializer(stddev=0.1))
b = tf.get_variable('b'),[out_dim],dtype=tf.float32,initializer=tf.constant_initializer(0))
net = tf.matmul(input_tensor,W)+b
在CPU上计算激活函数
with tf.device('/cpu:0'):
net = tf.nn.sigmoid(net)
//多CPU使用,多设备计算
//利用标号简单的区分并运行
在CPU0上计算
with tf.device('/cpu:0')
...
net = tf.nn.sigmoid(net)
在CPU1上计算
with tf.device('/cpu:1')
...
net = tf.nn.sigmoid(net)
//在集群上运行,需要在多个主机上准备多份代码,代码前面部分相同,后续有所不同
//定义多主机运行参数
这里的地址形式为IP:Port
cluster = tf.train.ClusterSpectf.train.ClusterSpec({
"worker":[
"xx.xx.xx.xx:2222", #/job:worker/task:0
"xx.xx.xx.xx:2222", #这里job的名称为自定义
"xx.xx.xx.xx:2222" #task编号同样需在Server中定义
],
"ps":[
"xx.xx.xx.xx:2222",
"xx.xx.xx.xx:2222"
]})
server = tf.train.Server(cluster, job_name="worker", task_index=0)
//定义第二个主机参数
这里的地址形式为IP:Port
cluster = tf.train.ClusterSpectf.train.ClusterSpec({
"worker":[
"xx.xx.xx.xx:2222", #/job:worker/task:0
"xx.xx.xx.xx:2222", #这里job的名称为自定义
"xx.xx.xx.xx:2222" #task编号同样需在Server中定义
],
"ps":[
"xx.xx.xx.xx:2222",
"xx.xx.xx.xx:2222"
]})
server = tf.train.Server(cluster, job_name="worker", task_index=1)
//不同设备的运行代码with tf.device('/job:worker/task:0/cpu:0'):
...
//将不同的任务分配到不同的计算节点上
//分配计算任务with tf.device(tf.train.replica_device_setter(
worker_device="/job:worker/task:%d" %task_index,cluster=cluster)
//函数replica_device_setter会将变量参数的定义部分自动定义到ps服务中,后续需要定义Session,用于执行这个过程
//多主机运行
定义句柄,迭代多少步后停止迭代
hooks = [tf.train.StopAtStepHook(last_step=1000000)]
MonitoredTrainingSession函数会完成会话初始化工作
保存checkpoint,恢复checkpoint,异常判断等
这里需要定义master主机,定义保存、控制操作的master
with tf.train.MonitroedTrainingSession(
master=server.target,
is_chief=(task_index==0),
checkpoint_dir="dir/to/cp",
hooks=hooks) as mon_sess:
...
注:在程序运行过程中,需要认为将程序分配到各个主机上,依次运行各个主机
//队列用于数据读取和处理,队列可以是先进先出队列,也可以是随机队列,用于随机化输出
//tf中队列的操作是对于训练前的过程而言的,有以下作用
1.多线程数据预处理并将其推入队列
2.在执行过程中,队列不断提供训练数据
//简单实例说明队列使用def simple_shuffle_batch(source,capacity,batch_size=10):
#定义随机序列
`queue = tf.RandomShuffleQueue(
capacity=capacity,
min_after_dequeue=int(0.9*capacity),
shapes=source.shape,
dtypes=source.dtype)`
#定义enqueue过程
`enqueue = queue.enqueue(source)`
#定义执行进程个数
`num_threads = 4
qr = tf.train.QueueRunner(queue,[enqueue]*num_threads)`
#声明Queue runner,使得其可以被执行
`tf.train.add_queue_runner(qr)`
#获取数据
`return queue.dequeue_many(batch_size)`
产生测试数据
input = tf.constant(list(range(100)))
input = tf.data.Dataset.from_tensor_slices(input)
input = input.make_one_shot_iterator().get_next()
定义函数
get_batch = simple_shuffle_batch(input,capacity=20)
定义session
with tf.train.MonitoredSession() as sess:
while not sess.should_stop():
print(sess.run(get_batch))
注:队列操作可以使得数据读取过程得到并行的优化,这对于提升程序的运行速度是很有利的。
//tf相关扩展
4.2.1 tf Layers
//全连接网络
layers定义全连接网络
net = tf.layers.dense(inputs=net, units=units, activation=tf.nn.relu)
卷积网络
net = tf.layers.conv2d(
inputs=net, #输入
filters=n_features, #输出特征数
kernel-size=[5, 5], #卷积核心大小
padding="same", #边界
activation=tf.nn.relu #激活函数
)
//前馈神经网络函数
二维最大池化
net = tf.layers.max_pooling2d(...)
二维平均池化
net = tf.layers.average_pooling2d(...)
二维卷积
net = tf.layers.conv2d(...)
dropout
net = tf.layers.dropout(...)
展开
net = tf.layers.flatten(...)
BN
net = tf.layers.batch_normalization(...)
4.2.2 tf Slim
卷积函数
def conv2d_layer(input_tensor, size=1, feature=128, name='conv1d'):
with tf.variable_scope(name):
shape = input_tensor.get_shape.as_list()
kernel = tf.get_variable('kernel', (size, size, shape[-1], feature), dtype=tf.float32, initializer=tf.truncated_normal_initializer(stddev=0.1))
b = tf.get_variable('b', [feature], dtype=tf.float32, initializer=tf.constant_initializer(0))
out = tf.nn.conv2d(input_tensor, kernel, strides=[1,2,2,1],padding='SAME')+b
return tf.nn.relu(out)
全连接函数
def full_layer(input_tensor, out_dim, name='full'):
with tf.variable_scope(name):
shape = input_tensor.get_shape.as_list()
W = tf.get_variable('W', (shape[1], out_dim), dtype=tf.float32, initializer=tf.truncated_normal_initializer(stddev=0.1))
b = tf.get_variabel('b', [out_dim], dtype=tf.float32, initializer=tf.constant_initializer(0))
out = tf.matmul(input_tensor, W)+b
return out
//slim实现卷积,tfv2取消该库
引入slim库
import tensorflow.contrib.slim as slim
定义卷积层
net = slim.conv2d(inputs, 16, 4, strides=2, activation_fn=tf.nn.relu, scope='conv1')
加入池化层
net = tf.nn.max_pool(net, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
net = slim.conv2d(net, 32, 4, strides=2, activation_fn=tf.nn.relu, scope='conv2')
net = tf.nn.max_pool(net, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
flatten层,用于将三维的图形数据展开成一维数据,用于全连接层
net = slim.flatten(net)
全连接层
y = slim.fully_connected(net, 10, activation_fn=line, scope='full', reuse=False)
4.2.3 tfLearn
//tflearn抽象层次更高,代码可读性更好,其是一个完整的生态
//基础网络架构
全连接
net = tflearn.fully_connected(...)
卷积
net = tflearn.conv_2d(...)
LSTM
net = tflearn.lstm(...)
dropout
net = tflearn.dropout(...)
//输入函数network = tflearn.input_data(shape=[None, 28, 28, 1], name='input')
//优化部分
定义优化过程
network = tflearn.layers.estimator.regression(
network,
optimizer='adam', #优化方法
learning_rate=0.01, #学习率
loss='categorical_crossentropy', #损失函数
name='target')
//利用tflearn完成手写数字的识别任务import tflearn
from tflearn.layers.core import input_data,dropout, fully_connected
from tflearn.layers.conv import conv_2d, , max_pool_2d
from tflearn.layers.normalization import local_response_normalization
from tflearn.layers.estimator import regression
载入并处理数据
import tflearn.datasets.mnist as mnist
X, Y, testX, testY = mnist.load_data(one_hot=True)
转换为二维图形
X = X.reshape([-1, 28, 28, 1])
testX = testX.reshape([-1, 28, 28, 1])
建立神经网络
network = tflearn.input_data(shape=[None, 28, 28, 1], name='input')
network = conv_2d(network, 32, 3, activation='relu', regularizer='L2')
network = max_pool_2d(network)
network = local_response_normalization(network)
network = fully_connected(network, 128, activation='tanh')
network = dropout(network, 0.8)
network = fully_connected(network, 256, activation='tanh')
network = dropout(network, 0.8)
network = fully_connected(network, 10, activation='softmax')
定义优化过程
network = regression(
network,
optimizer='adam',
learning_rate=0.01,
loss='categorical_crossentropy',
name='target')
训练过程
model = tflearn.DNN(network, tensorboard_verbose=0)
model.fit({'input':X}, {'target':Y}, n_epoch=20,
validation_set=({'input':testX}, {'target':testY}),
snapshot_step=100, show_metric=True, run_id='convnet_mnist')
//Keras代码可读性好,并且横跨多个机器学习框架,但其扩展性较差
//引入库from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D
//Keras直接顺序加入模型,无需通过数据方式进行传递
//基础网络层from keras.models import Sequential
model = Sequential()
加入卷积层
model.add(Conv2D(...))
加入池化层
model.add(MaxPooling2D(...))
加入全连接层
model.add(Dense(...))
dropout
model.add(Dropout(0.25))
//定义model后可直接加入多种层进行操作,同样其需要定义训练函数
//定义优化过程from keras.optimizers import SGD
定义迭代算法
sgd = SGD(lr=0.01, decay=1e-6, momentum=0.9, nesterov=True)
model.compile(loss='categorical_crossentropy', optimizer=sgd)
训练过程
model.fit(x_train, y_train, batch_szie=32, epochs=10)
评估训练效果
score = model.evaluate(x_test, y_test, batch_size=32)
//完整代码import keras
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D
from keras.optimizers import SGD
这里utils为自己定义的库函数,用于载入数据
import utils
X, Y, testX, testY = utils.load_data(one_hot=True)
model = Sequential()
定义神经网络过程
model.add(Conv2D(32, (3, 3), activation='relu', input_shape=(100, 100, 3)))
model.add(Conv2D(32, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(Conv2D(64, (3, 3), activatin='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
展开为一维数据用于全连接层
model.add(Flatten())
model.add(Dense(256, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(10, activation='softmax'))
梯度迭代算法
sgd = SGD(lr=0.01, decay=1e-6, momentum=0.9, nesterov=True)
model.compile(loss='categorical_crossentropy', optimizer=sgd)
训练过程
model.fit(x_train, y_train, batch_size=32, epochs=10)
效果评估
score = model.evaluate(x_test, y_test, batch_size=32)
4.3 Tensorboard与问题监控
//tensorboard最重要的作用就在于观察训练过程中的各种问题并改善,包括梯度消失、过拟合等问题
//获取所有可训练的参数var_list_w = [var for var in tf.trainable_variables() if 'w' in var.name]
var_list_b = [var for var in tf.trainable_variables() if 'b' in var.name]
//利用定义的梯度算法来计算梯度gradient_w = optimizer.compute_gradients(loss, var_list=var_list_w)
gradient_b = optimizer.compute_gradients(loss, var_list=var_list_b)
//返回的梯度是一个列表,可对其进行各种列表操作
//加入summary操作for idx, itr_g in enumerate(gradient_w):
variable_summaries(itr_g[0], 'layer%d-w-grad'%idx)
for idx, itr_g in enumerate(gradient_b):
variable_summaries(itr_g[0], 'layer%d-b-grad'%idx
for idx, itr_g in enumerate(var_list_w):
variable_summaries(itr_g, 'layer%d-w-grad'%idx)
for idx, itr_g in enumerate(var_list_b):
variable_summaries(itr_g, 'layer%d-b-grad'%idx)
4.4改善深度神经网络
//出现梯度消失一种最有效的方式就是进行BN操作
//batchnorm层net = tf.contrib.layers.batch_norm(net)
//加入BN层的神经网络
对于sigmoid激活函数来讲,BN操作效果可能不理想
net = slim.fully_connected(x, 4, activation_fn=tf.nn.sigmoid, scope='full1', reuse=False)
net = tf.contrib.layers.batch_norm(net)
net = slim.fully_connected(net, 8, activation_fn=tf.nn.sigmoid, scope='full2', reuse=False)
net = tf.contrib.layers.batch_norm(net)
net = slim.fully_connected(net, 8, activation_fn=tf.nn.sigmoid, scope='full3', reuse=False)
net = tf.contrib.layers.batch_norm(net)
net = slim.fully_connected(net, 4, activation_fn=tf.nn.sigmoid, scope='full4', reuse=False)
net = tf.contrib.layers.batch_norm(net)
net = slim.fully_connected(net, 3, activation_fn=tf.nn.sigmoid, scope='full5', reuse=False)
loss = tf.reduce_mean(tf.square(y-label))
4.5性能优化建议
//训练前的优化技巧
1.网络结构优化
Relu和BN能够有效加快神经网络训练速度
卷积核心的选取可以从大的卷积核心修改为多个小的卷积核心
将nxn修改为nx1+1xn,减少参数量,不同的输出内容之间可以进行concat
引入跨层支路解决梯度问题(ResNet)
2.初始值的选取
不好的初始值对训练的影响非常大,有效的初始化方法包括xavier初始化方法和He初始化方法
3.数据预处理
包括去均值和方差均衡
//训练过程中的优化技巧
1)优化算法的选择
Adam
2)学习率的选取
从大的步长开始进行迭代,逐步减少学习率
3)Batchsize选择
4)model ensembles
使用不同初始值同时训练多个模型,预测过程中将多个模型输出结果做平均,有效提升结果精度
5)dropout选择
从0.5附近进行调整,调整步长为0.05左右
//物体检测
1.传统检测方法
2001年,基于Haar特征和Adaboost检测方法引起轰动
2012年之前,三方面不断创新与优化:特征的设计更新、检测窗口的选择、分类器的设计更新
2.深度学习的物体检测
1)基于分类的物体检测
处理过程:图像被分解成多个小区域,每个小区域将运行一个分类算法以确定区域是否包含待检测物体,之后再在这个小区域的周围确认物体的边界框。代表算法:R-CNN、Fast-RCNN、Faster-RCNN
2) 基于回归的物体检测
将问题建模为回归问题,通过深度神经网络直接预测出边界框和所属类别的置信度。代表算法:SSD、YOLO模型
//YOLO模型
官网:https://pjreddie.com/darknet/yolo/
//选讲tiny YOLO v1模型,由9个卷积层和3个全连接层组成,每个卷积层都由卷积层、LeakyRelu和Max Pooling操作组成,前9个卷积层可被理解为特征提取器,最后三个全连接层可被理解为预测边界框的回归器。
参考论文:You Only Look Once:Unified, Real-Time Object Detection
参考实例:https://github.com/xslittlegrass/CarND-Vehicle-Detection
模型参数:45089374
深度学习框架:Keras 1.2.2
//构建YOLO模型网络结构import keras
from keras.models import Sequential
from keras.layers.convolutional import Convlution2D, MaxPooling2D
from keras.layers.advanced_activations import LeakyReLU
from keras.layers.core import Flatten, Dense, Activation, Reshape
from utils import load_weights, Box, yolo_net_out_to_car_boxes, draw_box
def construct_yolo_model():
keras.backend.set_image_dim_ordering('th')
model = Sequential()
model.add(Convolution2D(16, 3, 3, input_shape=(3, 448, 448), border_mode='same', subsample=(1, 1)))
model.add(LeakyReLU(alpha=0.1))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Convolution2D(32, 3, 3, border_mode='same'))
model.add(LeakyReLU(alpha=0.1))
model.add(MaxPooling2D(pool_size=(2, 2), border_mode='valid'))
model.add(Convolution2D(32, 3, 3, border_mode='same'))
model.add(LeakyReLU(alpha=0.1))
model.add(MaxPooling2D(pool_size=(2, 2), border_mode='valid'))
model.add(Convolution2D(64, 3, 3, border_mode='same'))
model.add(LeakyReLU(alpha=0.1))
model.add(MaxPooling2D(pool_size=(2, 2), border_mode='valid'))
model.add(Convolution2D(128, 3, 3, border_mode='same'))
model.add(LeakyReLU(alpha=0.1))
model.add(MaxPooling2D(pool_size=(2, 2), border_mode='valid'))
model.add(Convolution2D(256, 3, 3, border_mode='same'))
model.add(LeakyReLU(alpha=0.1))
model.add(MaxPooling2D(pool_size=(2, 2), border_mode='valid'))
model.add(Convolution2D(512, 3, 3, border_mode='same'))
model.add(LeakyReLU(alpha=0.1))
model.add(MaxPooling2D(pool_size=(2, 2), border_mode='valid'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(LeakyReLU(alpha=0.1))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(LeakyReLU(alpha=0.1))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(LeakyReLU(alpha=0.1
model.add(Flatten())
model.add(Dense(256))
model.add(Dense(4096))
model.add(LeakyReLU(alpha=0.1))
model.add(Dense(1470))
model.summary()
return model
注:网络的输入是形状为(3,448,448)的图像,其输出是一个1470维度的向量,它包含预测边界框、物体类别信息。1470矢量被分成三个部分,分别给出了所属类别概率、置信度和边框坐标。这三个部分进一步划分为49个小区域,与每个单元的预测相对应。
输出向量信息组织方式:probability:49*20=980
判断类别,20个类别confidence:49*2=98
是否包含物体(0,1)box coordinates:49*8=392
(x_min,y_min,x_max,y_max),(c_x,c_y,w,h)
8.4.3车辆图像数据探索
1.车辆视频数据预处理
//预处理及可视化图像def visualize_images():
imagePath = './test_images/test1.jpg'
image = plt.imread(imagePath)
#去除顶部和底部图片
`image_crop = image[300:650,500:,:]`
#将图片转换为模型所需要的输入格式
`resized = cv2.resize(image_crop, (448, 448))
f1,(ax11,ax22,ax33) = plt.subplot(1, 3, figsize=(16, 6))
ax11.imshow(image)
ax22.imshow(image_crop)
ax33.imshow(resized)
pylab.show()
return resized`
8.4.5迁移学习
通过迁移学习加载使用Pre-trained YOLO模型进行行车检测。具体做法是将pre-trained模型中的权重加载进之前构造的模型结构中,官网提供的权重,可以通过脚本解析成Keras能够加载的格式。
//加载YOLO模型权重
`def load_model_weights(model):
#预训练权重网址:https://pjreddie.com/darknet/yolo/
load_weights(model, './yolo-tiny.weights')`
//加载模型权重的具体逻辑def load_weights(model, yolo_weight_file):
data = np.fromfile(yolo_weight_file, np.float32)
data = data[4:]
index = 0
for layer in model.layers:
shape = [w.shape for w in layer.get_weights()]
if shape !=[]:
kshape, bshape = shape
bia = data[index:index+np.prod(bshape)].reshape(bshape)
index += np.prod(bshape)
ker = data[index:index:index+np.prod(kshape)].reshape(kshape)
index += np.prod(kshape)
layer.set_weights([ker, bia])
//模型推断
//使用模型进行在线推断,预测出车辆区域
`def inference_image(model, resized):
#转置
batch = np.transpose(resized, (2, 0, 1))
#将像素值变换到-1~1
batch = 2*(batch/255.) - 1
#将一张图片转为数组
batch = np.expand_dims(batch, axis=0)
out = model.predict(batch)
return out`
//绘制检测结果
//将上述的预测结果转换为边框坐标,同时基于阈值进行预测th = 0.17
boxes = yolo_net_to_out_to_car_boxes(out[0], threshold=th)
//定义box边框对象,判断是否保留预测的边框结果通过c,在图像上绘制车辆位置通过对象中的坐标信息
定义box类,存储边框信息和物体检测类别等信息
`class Box:
def init(self):
#x, y轴坐标
self.x, self.y = float(), float()
#边框宽度和长度
self.w, self.h = float(), float()
#置信度
self.c = float()
#所属类别概率
self.prob = float()`
//通过yolo_net_to_out_to_car_boxes方法,将预测出的Vector转换为Box对象信息。其核心逻辑是解析模型预测输出向量中的坐标、类别和置信度信息
//置信度大于阈值边界框则进行保留class_num = 6 #yolo模型可以预测多种类别,6为车辆所属类别
p = probs[grid, :] *bx.c
if p[class_num]>=threshold:
bx.prob = p[class_num]
boxes.append(bx)
//将结果绘制在图像上def visualize_image_car_detection(boxes):
imagePath = './test_images/test1.jpg'
image = plt.imread(imagePath)
f, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 6))
ax1.imshow(image)
ax2.imshow(draw_box(boxes, plt.imread(imagePath), [[500, 1280],[300,650]]))
pylab.show()
//将边框绘制在图像上def draw_box(boxes, im, crop_dim):
imgcv = im
[xmin, xmax] = crop_dim[0]
[ymin, ymax] = crop_dim[1]
for b in boxes:
h, w, _ = imgcv.shape
left = int((b.x-b.w/2.)*w)
right = int((b.x+b.w/2.)*w)
top = int((b.y-b.h/2.)*h)
bot = int((b.y+b.h/2.)*h)
left = int(left*(xmax-xmin)/w+xmin)
right = int(right*(xmax-xmin)/w+xmin)
top = int(top*(ymax-ymin)/h+ymin)
bot = int(bot*(ymax-ymin)/h+ymin)
if left<0 : left=0
if right>w-1 : right=w-1
if top<0 : top=0
if bot>h-1 : bot=h-1
thick = int((h+w)//150)
cv2.rectangle(imgcv, (left, top), (right, bot), (255,0,0), thick)
return imgcv
8.5.1英伟达End to End模型
End to End的好处:通过缩减人工预处理和后续处理,尽可能使模型从原始输入到输出,使得其根据网络模型能够有足够多的空间进行自动调节,从而减少基于规则的复杂变化。
缺点:可解释性较差,准确度和精度不容易受控制。
//构建英伟达模型import tensorflow as tf
import keras
from keras.models import Sequential
from keras.layers import Dense, Activation, Flatten, Lambda
from keras.layers import Conv2D, Dropout
from keras import losses
def nvida_model():
model = Sequential()
model.add(Lambda(lambda x: x/127.5-1., input_shape=(img_height, img_width, img_channels)))
model.add(Conv2D(24, kernel_size=(5, 5), strides=(2, 2), padding='valid', kernel_initializer='he_normal', activation='elu'))
model.add(Conv2D(36, kernel_size=(5, 5), strides=(2, 2), padding='valid', kernel_initializer='he_normal', activation='elu'))
model.add(Conv2D(48, kernel_size=(5, 5), strides=(2, 2), padding='valid', kernel_initializer='he_normal', activation='elu'))
model.add(Conv2D(64, kernel_size=(5, 5), strides=(2, 2), padding='valid', kernel_initializer='he_normal', activation='elu'))
model.add(Conv2D(64, kernel_size=(5, 5), strides=(2, 2), padding='valid', kernel_initializer='he_normal', activation='elu
model.add(Flatten())
model.add(Dense(1164, kernel_initializer='he_normal', activation='elu'))
model.add(Dense(100, kernel_initializer='he_normal', activation='elu'))
model.add(Dense(50, kernel_initializer='he_normal', activation='elu'))
model.add(Dense(10, kernel_initializer='he_normal', activation='elu'))
model.add(Dense(1, kernel_initializer='he_normal'))
model.compile(loss='mse', optimizer='Adadelta')
return model
//8.5.3数据分析
1)转向控制数据分布
绘制转向分布
def steering_distribution():
wheel_sig = pd.read_csv(params.data_dir+'/epoch01_steering.csv')
wheel_sig.head()
wheel_sig.wheel.hist(bins=50)
2)数据变化幅度
绘制转向变化幅度
def angel_visualize():
wheel_sig = pd.read_csv(params.data_dir+'/epoch01_steering.csv')
wheel_sig.plot(x='frame', y='wheel')
plt.show()
8.5.4读入视频,并处理图像
//使用OpenCV从视频中提取图像,以及与其对应的转向角度并返回
提取图像并处理
`imgs = []
wheels = []
epochs = [10]
for epoch in epochs:
vid_path = utils.join_dir(params.data_dir, ‘epoch{:0>2}_front.mp4’.format(epoch))
assert os.path.isfile(vid_path)
frame_count = frame_count_func(vid_path)
cap = cv2.VideoCapture(vid_path)
for frame_id in range(frame_count):
while True:
#通过OpenCV中的VideoCapture进行视频中图像的提取
ret, img = cap.read()
if not ret:
break
#用户可以自定义对图像的处理、扩展和增强操作
img = a_image_convert.img_preprocess(img, color_mode, flip=False)
imgs.append(img)
csv.path = os.path.join(data_dir, 'epoch{:0>2}_steering.csv'.format(epoch))
rows = pd.read_csv(csv.path)
yy = rows['wheel'].values
wheels.extend(yy)
cap.release()
imgs = np.array(imgs)
wheels = np.array(wheels)
wheels = np.reshape(wheels, (len(wheels), 1)
return imgs, wheels`
8.5.5深度学习模型构建与训练
//训练模型
`def training(model, X_train_RGB, y_train_RGB):
RGB_model = model
time_start = time.time()
#fit the model
RGB_history = RGB_model.fit(X_train_RGB, y_train_RGB, epochs=30, batch_size=batch_size)
return RGB_model, RGB_history`
//可视化结果
将训练过程中的loss误差进行可视化
def visualize_label(RGB_history):
print(RGB_history.history['loss']
plt.figure(figsize=(9, 6))
plt.plot(RGB_history.history['loss'])
plt.title('model loss')
plt.ylabel('Loss', fontsize=12)
plt.xlabel('Epoch', fontsize=12)
plt.legend(['train RGB'], loc='upper right')
plt.grid()
plt.show()
//可视化
//数据的绘图过程就是将前面所得到的一系列数据,通过静态、动态的二维、三维图形进行展示
1.Matplotlib
//绘制y=sinx图像import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 4*np.pi, 1000)
y = np.sin(x)
plt.plot(x,y)
//利用API,绘制更加审美要求的图像
`import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
设置图片风格
mpl.style.use(‘seaborn-darkgrid’)
定义曲线
x = np.linspace(0, 4*np.pi, 100)
y1 = np.sin(x)
y2 = np.sin(x+1)
y3 = np.sin(x+2)
绘图
plt.plot(x, y1, color=’#009900’, lw=6, alpha=0.6)
plt.plot(x, y2, color=’#990000’, lw=6, alpha=0.6)
plt.plot(x, y3, color=’#000099’, lw=6, alpha=0.6)
展示
plt.show()`
9.4ECharts
//ECharts提供了常规的折线图、柱状图、散点图、饼图、K线图等等,功能强大。
//ECharts图形绘制
略
//文本向量化
//文本向量化函数
文本TfIdf向量化
from sklearn.feature_extraction.text import TfidfVectorizer
vectorizer = TfidVectorizer()
vectors = vectorizer.fit_transform(datas)
//文本向量化的数据进行降维
//LDA降维from sklearn.decomposition import LatentDirichletAllocation
lda = LatenDirichletAllocation(n_components=n_topic, max_iter=5,
learning_method = 'online',
learning_offset = 50.,
radom_state = 0)
用LDA方法降维数据
dr_vectors = lad.fit_transform(vectors)
9.6三维可视化
//ECharts地图柱状图myChart.setOption({
visualMap: {
show: flase,
calculable: true,
realtime: false,
inRange: {
color: ['#313695', '#4575b4', '#74add1', '#abd9e9',
'#e0f3f8', '#ffffbf', '#fee090', '#fdae61',
'#f46d43', '#d73027', '#d73027', 'a50026']
},
outOfRange: {
colorAlpha: 0
},
max: linedata[1]
},
...
series: [{
type: 'bar3D',
shading: 'realistic',
coordinateSystem: 'mapbox',
barSize: 0.2,
silent: true,
data: linedata[0]
}]
});
//利用Matplotlib完成对三维数据的可视化任务from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
from matplotlib import cm
import matplotlib.style as style
style.use('seaborn-darkgrid')
定义三维画布
fig = plt.figure()
ax = fig.gca(projection='3d')
获取数据
X, Y, Z = axes3d.get_test_data(0.05)
绘制surface
ax.plot_surface(X, Y, Z, rstride=8, cstride=8, alpha=0.3)
绘制等值线
cst = ax.contourf(X, Y, Z, zdir='z', offset=-100, cmap=cm.coolwarm)
cst = ax.contourf(X, Y, Z, zdir='x', offset=-40, cmap=cm.coolwarm)
cst = ax.contourf(X, Y, Z, zdir='y', offset=40, cmap=cm.coolwarm)
plt.show()
9.7动态可视化
//Matplotlib中用于数据动态演示的方法为animation,其可以通过函数进行简单的调用,以进行动态图形的演示工作
//动画展示import matplotlib.animation as animation
animation.FuncAniamtion(
...
)
//动态可视化的展示方式是在普通的图表之上通过不断地修改数据并进行展示,这种修改可以通过setOption而得到的,在实现上可以通过函数递归的方式实现动态数据的可视化工作function update(){
myChart.setOption(...);
setTimeout(update, UPDATE_DURATION);
}
update();
//优化实践
10.1通用深度神经网络训练优化建议
1)通用的较为优化的训练过程
1.将问题转换为相似的经典问题场景,参照paper中的配置和调优技巧进行最初的实验与优化
2.优化算法:选用随机梯度下降(SGD)算法,虽然批量梯度下降(BGD)相比SGD有一些优势,但是在处理大规模数据时,SGD及其优化变种更加简单和快速。
3.随机Shuffle样本:应尽量避免连续处理的样本属于同一类别的情况。尽量选择当前样本能让模型产生较大的误差,而不是较小的误差
4.规范化数据:输入的每个变量均值最好趋近于0.变换输入变量,使其协方差相近,变量间尽量不要相关
5.激活函数的选取:相比Sigmoid函数,tanh和Relu有更好的收敛速度。
6.权重初始化:可以随机通过一种分布,均值为0.
7.选择学习率:每个权重都可以选取属于自己的学习率。处于低层的权重学习率最好大于高层的权重学习率。学习率最好正比于每个单元的输入数量。
2)CNN训练过程中通常关注的优化点和参数
一般比较关注:Learning Rate,Weight Decay,Momentum,Batchsize,Init Weights,数据增强
eg:在Resnet中,使用SGD优化算法优化方法训练,mini-batch的大小设置为256,学习率初始化为0.1.随着训练进行,当Loss不再下降,会每次自适应以10倍进行缩减学习率。模型训练用了60x10^4轮迭代。Weight Decay设置为0.0001,同时设置momentum为0.9
3)RNN训练过程中通常关注的优化点和参数
一般比较关注:SGD,正则化,规范化梯度,Pad Sentence,Init Weight, Batch Size, Embedding输入,输出控制,Vacabulary Size, Sampled Softmax
eg:Google发布的TTS模型TACOTRON为例
10.1.1 过拟合和欠拟合
欠拟合:若训练集和测试集的误差有收敛但很高时,则为高偏差
过拟合:若训练集和测试集的误差较大时,则为高方差
解决过拟合的方法:
正则化,数据增强,Early Stop, Dropout, Batch Normalization
解决欠拟合的方法:
1.使用更加复杂的深度学习网络架构
2.添加其他特征项,有时候模型出现欠拟合的情况是因为特征项不够导致的,可以添加其他特征项来很好的解决这个问题
3.减少正则化参数和组件,正则化的目的是用来防止过拟合。
10.1.2数据增强
//数据增强的根本原因在于机器在学习的过程中会在模型中遇到大量的参数,同时为了防止过拟合
1)对于图像数据,可采取:
1.图像平移:使得网络学习到平移不变的特性
2.图像旋转:使得网络学习到旋转不变的特性
3.图像亮度变化
4.裁剪
5.缩放
6.图像模糊:用不同的卷积模板产生模糊图像
2)语音识别中对输入数据添加随机噪声等方式
3)NLP中最常用的方式就是进行近义词替换等方式
4)噪声注入,可以对输入添加噪声,也可以对隐藏层或者输出层添加噪声
10.1.3梯度消失
//实验数据显示了深度神经网络在训练过程中,随着epoch的增加各隐藏层的学习率变化。前面隐藏层的学习速度要低于后面的隐藏层
//梯度消失的原因:根据链式法则,如果每一层神经元对上一层输出的偏导乘上权重结果都小于1的话,那么即使这个结果是0.99,在经过足够多层的传播后,误差对输入层的偏导也会趋近于0
解决梯度消失的策略:
1.BN
2.RNN中使用LSTM:适用于RNN,门控制和长时记忆可缓解和解决梯度消失问题
3.激活函数Relu:新的激活函数解析性质更好,其在一定程度上克服了sigmoid函数和tanh函数的梯度消失问题。
4.在RNN反向传播过程中减少时间步长度。
10.1.4初始化权重
//在参数解空间内,好的权重初始化方式,意味着离全局最小值更近。
1.高斯初始化,为权重初始化较小的值,权重按照高斯分布随机进行初始化,固定均值和方差
2.Xaiver更新方法,使用tanh为激活函数,效果较好。进行梯度更新时,收敛速度较快,然而没有考虑Relu
3.MSRA方法,适用于从头训练深层深度神经网络的网络结构。权重以高斯分布随机进行初始化,方差需要考虑空间过滤器的大小和过滤器数量的影响。
10.1.5优化算法
近些年最常用的是采用Adam优化算法,也可以采用自适应学习率的方法实现快速收敛。
10.1.6超参数选择
一些实践经验:
1.在验证集上进行调参
2.优先调Learning Rate
3.通过初期设计卷积层尽量深、卷积核尽量多的模型,强行让模型拟合训练集,这时会出现过拟合,之后通过Dropout、正则化和Data Augument等等方式去改善模型结果
4.调整模型的层数和卷积核数量
//通过Scikit-learn的网格搜索库进行参数调优实例
1.常见搜索参数
学习率、Dropout、Epochs和神经元数量
2.数据集下载
数据集为Pima Indians Onset of Diabetes分类数据集
下载地址:https://archive.ics.uci.edu/ml/machine-learning-databases/pima-indians-diabetes/
3.搜索最优batchsize和epochs
//以20的步长,从10到100逐步评估不同的微型批尺寸,epochs分别设置为10、50、100import numpy
from sklearn.grida_search import GridSearchCV
from keras.models import Sequential
from keras.layers import Dense
from keras.wrappers.scikit_learn import KerasClassifier
Function to create model, required for KerasClassifier
`def create_model():
#create model
model = Sequential()
model.add(Dense(12, input_dim=8, activation='relu'))
model.add(Dense(1, activation='sigmoid'))`
#compile model
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
return model
`#fix random seed for reproducibility
seed = 7
numpy.random.seed(seed)
load dataset
dataset = numpy.loadtxt(“pima-indians-diabetes.csv”, delimiter=’,’)
split into input (x) and output (Y) variables
X = [:, 0:8]
Y = [:, 8]
create model
model = KerasClassifier(build_fn=create_model, verbose=0)
define the grid search parameters
batch_size = [10, 20, 40, 60, 80, 100]
epochs = [10, 50, 100]
param_grid = dict(batch_size=batch_size, nb_epoch=epochs)
grid = GridSearchCV(estimator=model, param_grid=parm_grid, n_jobs=-1)
grid_result = grid_fit(X,Y)
summarize results
print(“Best: %f using %s” % (gridresult.best_score, gridresult.best_params))
for params, mean_score, scores in grid_result.grid_scores:
print(“%f (%f) with: %r” % (scores.mean(), scores.std(), params))`
10.2深度学习系统性能优化建议
10.2.1输入及预处理流水线优化
输入流水线:从磁盘读取图像,将JPEG预处理为张量,进行数据预处理裁剪、翻转等,然后进行批处理操作
1.在CPU端进行预处理
//在CPU端上放置输入预处理操作可以显著提高性能,GPU专注训练
//控制代码在CPU端执行
`with tf.device(“/cpu:0”):
# function to get and process data.
distored_inputs = load_and_preprocess_images()`
2.使用大文件
读取大量的小文件会显著影响I/O性能
1)转换为TFRecord格式
2)小数据集加载到内存
10.2.2数据格式
NHWC的方存局部性更好(每三个输入像素即可得到一个输出像素),NCHW则必须等所有通道输入都准备好后才能得到最终的输出结果,需要占用较大的临时空间。
tf默认NHWC格式,Nvidia cuDNN默认NCHW格式
注:设计网络时充分考虑这两种格式,最好能够灵活切换,在GPU上训练时使用NCHW格式,在CPU上做预测时使用NHWC格式
10.2.3编译优化
//通过bazel命令对特定平台对tf进行编译bazel build -c opt --copt=-march="brodewell" --config=cuda
//tensorflow/tools/pip_package:build_pip_package
10.2.4GPU性能瓶颈诊断
//参考如下分析步骤对作业进行优化
1)对代码进行性能分析
2)找到运行慢的阶段
3)分析慢的原因
4)修改成更快的实现
5)再次对代码进行性能分析
//处理器有两个关键的性能瓶颈:浮点计算量和内存吞吐量。
//可通过以下工具进行深度学习作业的性能分析
1.Tensorflow性能分析工具Timeline(获取执行图中每个节点的执行时间)
1)创建metadata运行时对象
2)获取运行时信息创建Timeline对象
3)将Timeline对象写入json文件
4)Chrome加载trace的json文件
//tensorflow使用Timeline进行性能分析import tensorflow as tf
from tensorflow.python.client import timeline
x = tf.random_normal([1000, 1000])
y = tf.random_normal([1000, 1000])
res = tf.matmul(x, y)
#run the graph with full trace option
with tf.Session() as sess:
run_options = tf.RunOptions(trace_level=tf.RunOptions.FULL_TRACE)
run_metadata = tf.RunMetadata()
sess.run(res, options=run_options, run_metadata=run_metadata)
#create Timeline variable,then write it into json file
t1 = timeline.Timeline(run_metadata.step_stats)
ctf = t1.generate_chrome_trace_format()
with open('timeline.json', 'w') as f:
f.write(ctf)
可以打开谷歌chrome浏览器,转到chrome://tracing页并加载timeline.json文件,接下来,可以进行程序的profiling
2.常用的GPU分析工具
1)nvprof是英伟达性能分析工具
2)nvvp则是带GUI的英伟达可视化性能分析工具
10.2.5CPU瓶颈优化
1)多线程方式优化
以下两个针对tensorflow的配置可以通过适配线程池进行CPU的性能优化
intra_op_parallelism_threads:对tf操作符内部的任务进行并行化
inter_op_parallelism_threads: 控制多个运算符之间的并行化运算
//多线程优化config = tf.ConfigProto()
config.intra_op_parallelism_threads = 22
config.inter_op_parallelism_threads = 22
tf.session(config=config)
2)使用SIMD高级指令集
参考tf官方文档的”Performance Guide”章节
10.2.6模型压缩
模型小型化:从模型权重的角度进行压缩和从网络架构的角度进行压缩
网络架构角度:提出新的网络结构或卷积方法进行压缩优化,如SqueezeNet, MobileNets等
模型权重角度:一般是在已经训练好的模型上进行裁剪,然后fine-tuning到原有模型的准确率,一般的优化方式包括剪枝、权值共享、神经网络二值化等。
10.3工程实践建议
10.3.1Model格式转换
框架间的模型转换
参考链接:
1.https://github.com/ysh329/deep-learning-model-convertor
2.https://github.com/Microsoft/MMdnn
10.3.2迁移学习(Transfer Learning)
其思想是将训练好的模型参数迁移到新的模型来帮助新模型的训练和预测。
//通过MNIST数据集0~4的数字训练一个模型,然后将模型迁移到5~9数据集上进行迁移学习
1)在MNIST数据集上训练一个简单的卷积神经网络,只预测0~4的数字
2)将训练好的预测0~4数据集的模型,应用到5~9数据集上。对模型冻结卷积层参数,Fine-Tuning全连接层。
//keras迁移学习实例from __future__ import print_function
import datetime
import keras
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation, Flatten
from keras.layers import Conv2D, MaxPooling2D
from keras import backend as K
now = datetime.datetime.now
batch_size = 128
num_classes = 5
epochs = 5
`#input images dimensions
img_rows, img_cols = 28, 28
number of convolutional filters to use
filters = 32
size of pooling area for max pooling
pool_size = 2
convolution kernel size
kernel_size = 3`
if K.image_data_format()=='channels_first':
input_shape = (1, img_rows, img_cols)
else:
input_shape = (img_rows, img_cols, 1)
def train_model(model, train, test, num_classes):
x_train = train[0].reshape((train[0].shape[0],)+input_shape)
x_test = test[0].reshape((test[0].shape[0],)+input_shape)
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255
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 matrics
y_train = keras.utils.to_categorical(train[1], num_classes)
y_test = keras.utils.to_categorical(test[1], num_classes)
model.compile(
loss = 'categorical_crossentropy',
optimizer = 'adadelta',
metrics = ['accuracy']
)
t = now()
model.fit(x_train, y_train,
batch_size = batch_size,
epochs = epochs,
verbose = 1,
validation_data = (x_test, y_test))
print('Training time: %s' %(now() -t))
score = model.evaluate(x_test, y_test, verbose=0)
print('Test score:', score[0])
`#the data,shuffled and spilt between train and test sets
(x_train, y_train), (x_test, y_test) = mnist.load-data()
create two datasets one with digits below 5 and one with 5 and above
x_train_lt5 = x_train[y_train<5]
y_train_lt5 = x_train[y_train<5]
x_test_lt5 = x_test[y_test<5]
y_test_lt5 = y_test[y_test<5]`
x_train_get5 = x_train[y_train>=5]
y_train_get5 = y_train[y_train>=5]-5
x_test_get5 = x_test[y_test>=5]
y_test_get5 = y_test[y_test>=5]-5
#define two groups of layers:feature(convolutions) and classification(dense)
feature_layers = [
Conv2D(filters, kernel_size,
padding='valid',
input_shape=input_shape),
Activation('relu'),
Conv2D(filters, kernel_size),
Activation('relu'),
MaxPooling2D(pool_size=pool_size),
Dropout(0.5),
Flatten()]
classification_layers=[
Dense(128),
Activation('relu'),
Dropout(0.5),
Dense(num_classes),
Activation('softmax')]
#create complete model
model = Sequential(feature_layers+classification_layers)
#train model for 5-digit classification(0~4)
train_model(model,
(x_train_lt5, y_train_lt5),
(x_test_lt5, y_test_lt5), num_classes)
#freeze feature layers and rebuild model
for l in feature_layers:
l.trainable = False
#transfer: train dense layers for new classification task(5~9)
train_model(model,
(x_train_gte5, y_train_get5),
(x_test_get5, y_test_get5), num_classes)