Deep Learning Noob Guide
1. 说明
本文主要是对自己入坑deep learning时的历程做一个简单的总结,并回顾本人承担的2020年集创赛神经网络算法部分的开发任务。
时间回到2020年初,我那会刚打算着手做一些深度学习相关的应用,恰好那年立项的大创题目是一个手势识别系统的实现,所以我很自然的考虑到用卷积神经网络(CNN)去做这个任务,但由于知识受限(并未在学校内学习过有关课程),除了简单看过一些博客,短视频之类的资料,我对深度学习并没有多么深刻的理解,所以我采用多线并进的方案去尝试掌握这门知识。
事实证明学习起来还是挺快的,从看书建立基本观念,到写一些简单的demo,再到最后写出来比赛用的工程,总共就是3个月左右的时间。本文将沿着我自己入门的历程,从理论部分,实验部分,实战部分三个环节进行介绍。同时也会补充一些后面继续做项目&学理论时建立的更高视角。
2. 理论部分
和西瓜书、花书、吴恩达的网课这类常见入门方式不同,我入门时靠的是从网上找到的一本名叫《白话深度学习与Tensorflow》的小书。当然书里讲实现用的是tensorflow1,这东西早就进历史的垃圾堆了,所以我建议是找一本新一点印出来的同时讲理论和实现的小书,最好是基于pytorch的,然后把理论部分给好好过一过,不过毕竟不是专做算法的,把数学吃得那么透在我看来并不是很有意义。对神经网络建立一个初步概念对于数学实际上没有太高的要求,有一些微积分、线性代数和概率论的底子就能快速理解到底在干啥。
目前deep learning的主战场,一个在cv(计算机视觉)一个在nlp(自然语言处理),当然rl(强化学习)那边也收到了一定的冲击,但最主要的是前两者。这俩领域我的建议是选修一下课程,学习速度会快很多,也能有更深入的理解。因为cv和nlp的范式是被deep learning给颠覆了的,不先去学习一下经典方法是不会感受到这种颠覆性的——传统特征工程+线性分类器的范式为何会被deep learning取代,这种取代在哪些场景下是ok的,哪些场景下又不ok。大部分网上的资料去讲这俩的时候,会直接讲CNN,RNN,LSTM,Transformer这些模型,但是其实了解来龙去脉是很重要的,毕竟deep learning并不是万能的,尤其是在一些涉及可解释性,或者小样本,或者数据集质量不高的场景。
总结:
-
找一本类似于《白话深度学习与Tensorflow》的小书,不要选大部头。
-
选修cv(计算机视觉)和nlp(自然语言处理)的课程,要既讲经典方法也讲deep learning方法的,例如国科大的两门同名课程。
3. 实验部分
第一步,学python,因为现在业界和学术界主流使用的deep learning框架的主流语言就是python。作为一门解释型语言python极其容易上手,学习成本很低,搭环境也可以通过强大的包管理器迅速搞定。
接着,具体学习哪种框架,我认为优先pytorch,因为学术界用的最多,大部分主流的模型都能在github上找到pytorch的实现,有闲心可以学一下tensorflow2或者其他的框架。其实思想都是一样的,api不同罢了,各种框架的优劣由于不专门做算法所以也不好评价。
安装好了框架之后得要跑几个demo练手吧,网上的教程一大堆,但mnist作为深度学习的hello world是母庸置疑的。所以,找到一个用自己安装的深度学习框架编写的mnist手写数字识别任务的实现的教程,然后一步步跟着做吧。
总结:
- 学python,掌握基本语法即可。
- 找一个deep learning框架,安装,学习基本api。
- 跑mnist手写数字识别。
4. 实战部分
好了,重头戏到来,我自己的经历是花了一个月左右,以三天打鱼两天晒网的效率完成了理论学习,并写出了我的第一个深度学习程序(mnist),接下来我需要干的事情是:自己制作所需要的数据集,设计一个卷积神经网络,然后训练它,再把权重导出来量化,最后部署到fpga上面。所以我的就顺利成章的按照如下的步骤去做了:
- 数据集制作:
我要做的是一个手势识别的任务,于是去网上找了一些开源数据集,但很遗憾的都不符合我这边的需求。我希望弄一个轻量的,只有6个手势的数据集,并且最好是单通道的,就像mnist数据集那样,所以最后我通过opencv写了一个自动采集+缩放+二值化的脚本,实现了需求。
每类的图片我拍了2000张左右(就各种倒腾姿势的拍),然后通过数据强化(旋转,翻转)又把每张图变成八张,最后拿着这个数据集按照2:8的比例划分了测试集和训练集。这个过程有点体力活,不过还挺有意思。
- 网络设计&&训练
我直接通过前面写过的CNN+mnist工程去魔改,和做硬件的队友一直在battle,算网络结构的参数总量,会花掉的硬件资源这些,最后裁剪出来一个非常小的网络。做训练其实很简单,只需要把原来读取mnist的部分换成读取自己的数据集就ok了。中间碰到了训练没收敛的问题,后来发现把batch改小(因为我电脑内存不够大),epoch改大以后效果能够好很多。几轮调试下来,150个epoch后精度上了90,我就基本满意了,做了一些小的让精度提升的尝试后就基本把网络定型了。
这段古旧的代码如下,尽管已经失去了代码上的参考意义,并且写得相当烂,但其精神依然值得学习(指找别人的代码然后学习理解再魔改成自己的):
DatasetGenerate.py
import numpy as np
import os
import tensorflow as tf
from PIL import Image
# small dataset ----> train/test
# cwd = 'D:/PycharmProjects/CNNHandGestureRecognization/Dataset_Final/train'
# cwd = 'D:/PycharmProjects/CNNHandGestureRecognization/Dataset_Final/test'
# enhanced dataset ------> train enhanced/test enhanced
# cwd = 'D:/PycharmProjects/CNNHandGestureRecognization/Dataset_Final_Enhanced/train'
cwd = 'D:/PycharmProjects/CNNHandGestureRecognization/Dataset_Final_Enhanced/test'
classes = ['1', '2', '3', '4', '5', '6']
# 制作TFRecords数据
def create_record():
# writer = tf.python_io.TFRecordWriter("train.tfrecords")
# writer = tf.python_io.TFRecordWriter("test.tfrecords")
# writer = tf.python_io.TFRecordWriter("train_enhanced.tfrecords")
writer = tf.python_io.TFRecordWriter("test_enhanced.tfrecords")
for index, name in enumerate(classes):
class_path = cwd + "/" + name + "/"
for img_name in os.listdir(class_path):
img_path = class_path + img_name
img = Image.open(img_path)
img = img.resize((64, 64))
img_raw = img.tobytes()
print(img_path, index)
example = tf.train.Example(
features=tf.train.Features(feature={
"label": tf.train.Feature(int64_list=tf.train.Int64List(value=[index])),
'img_raw': tf.train.Feature(bytes_list=tf.train.BytesList(value=[img_raw]))
}))
writer.write(example.SerializeToString())
writer.close()
# -------------------------------------------------------------------------
# 读取二进制数据
def read_and_decode(filename):
# 创建文件队列,不限读取的数量
filename_queue = tf.train.string_input_producer([filename])
# create a reader from file queue
reader = tf.TFRecordReader()
# reader从文件队列中读入一个序列化的样本
_, serialized_example = reader.read(filename_queue)
# get feature from serialized example
# 解析符号化的样本
features = tf.parse_single_example(
serialized_example,
features={
'label': tf.FixedLenFeature([], tf.int64),
'img_raw': tf.FixedLenFeature([], tf.string)
})
label = features['label']
img = features['img_raw']
img = tf.decode_raw(img, tf.uint8)
img = tf.reshape(img, [64, 64, 1])
# 如果要将数据集打印出来则将下一句注释,如果要在训练中使用则取消
img = tf.cast(img, tf.float32) * (1. / 255)
label = tf.cast(label, tf.int32)
return img, label
# --------------------------------------------------------------------------
# ---------主程序----------------------------------------------------------
if __name__ == '__main__':
create_record()
# batch = read_and_decode('train.tfrecords')
# batch = read_and_decode('test.tfrecords')
# batch = read_and_decode('train_enhanced.tfrecords')
batch = read_and_decode('test_enhanced.tfrecords')
init_op = tf.group(tf.global_variables_initializer(), tf.local_variables_initializer())
with tf.Session() as sess: # 开始一个会话
sess.run(init_op)
coord = tf.train.Coordinator()
threads = tf.train.start_queue_runners(coord=coord)
for i in range(339):
example, lab = sess.run(batch) # 在会话中取出image和label
# 如果要生成图片,8bit图需要把read_and_decode中改成reshape([64,64])
# img = Image.fromarray(example, 'L') # 这里Image是之前提到的
# img.save(cwd + '/test/' + str(i) + '_Label_' + str(lab) + '.png') # 存下图片;注意cwd后边加上‘/’
print(example, lab)
coord.request_stop()
coord.join(threads)
sess.close()
Train.py
import tensorflow as tf
import numpy as np
import DatasetGenerate
epoch = 150
# batch_size = 300
batch_size = 1000
# dataset_size = 9000
dataset_size = 72000
def one_hot(labels,Label_class):
one_hot_label = np.array([[int(i == int(labels[j])) for i in range(Label_class)] for j in range(len(labels))])
return one_hot_label
#convolution layer
def conv2d(x,W):
return tf.nn.conv2d(x, W, strides=[1,1,1,1], padding='SAME')
#max_pool layer
def max_pool_4x4(x):
return tf.nn.max_pool(x, ksize=[1,4,4,1], strides=[1,4,4,1], padding='SAME')
# input
x = tf.placeholder(tf.float32, [batch_size,64,64,1])
y_ = tf.placeholder(tf.float32, [batch_size,6])
# first convolution and max_pool layer
W_conv1 = tf.Variable(tf.truncated_normal([3,3,1,4], stddev = 0.02), name="w1")
b_conv1 = tf.Variable(tf.constant(0.0, shape=[4]), name="b1")
h_conv1 = tf.nn.relu(conv2d(x, W_conv1) + b_conv1)
h_pool1 = max_pool_4x4(h_conv1)
# second convolution and max_pool layer
W_conv2 = tf.Variable(tf.truncated_normal([3,3,4,2], stddev = 0.02), name="w2")
b_conv2 = tf.Variable(tf.constant(0.0, shape=[2]), name="b2")
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
h_pool2 = max_pool_4x4(h_conv2)
# 变成全连接层,用一个MLP处理
reshape = tf.reshape(h_pool2,[batch_size, -1])
dim = reshape.get_shape()[1].value
W_fc1 = tf.Variable(tf.truncated_normal([dim, 6], stddev = 0.02), name="wfc1")
b_fc1 = tf.Variable(tf.constant(0.0, shape=[6]), name="bfc1")
# h_fc1 = tf.nn.relu(tf.matmul(reshape, W_fc1) + b_fc1)
y_conv = tf.nn.softmax(tf.matmul(reshape, W_fc1) + b_fc1)
# 损失函数及优化算法
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y_conv), reduction_indices=[1]))
train_step = tf.train.AdamOptimizer(0.0001).minimize(cross_entropy)
correct_prediction = tf.equal(tf.argmax(y_conv,1),tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction,tf.float32))
# saver
saver = tf.train.Saver()
img, label = DatasetGenerate.read_and_decode("train_enhanced.tfrecords")
img_test, label_test = DatasetGenerate.read_and_decode("test_enhanced.tfrecords")
# img, label = DatasetGenerate.read_and_decode("train.tfrecords")
# img_test, label_test = DatasetGenerate.read_and_decode("test.tfrecords")
# 使用shuffle_batch可以随机打乱输入
img_batch, label_batch = tf.train.shuffle_batch([img, label],
batch_size=batch_size, capacity=2000,
min_after_dequeue=1000)
img_test, label_test = tf.train.shuffle_batch([img_test, label_test],
batch_size=batch_size, capacity=2000,
min_after_dequeue=1000)
init = tf.initialize_all_variables()
t_vars = tf.trainable_variables()
print(t_vars)
with tf.Session() as sess:
sess.run(init)
coord = tf.train.Coordinator()
threads=tf.train.start_queue_runners(sess=sess,coord=coord)
batch_idxs = int(dataset_size/batch_size)
for i in range(epoch):
for j in range(batch_idxs):
val, l = sess.run([img_batch, label_batch])
l = one_hot(l,6)
_, acc = sess.run([train_step, accuracy], feed_dict={x: val, y_: l})
print("Epoch:[%4d] [%4d/%4d], accuracy:[%.8f]" % (i, j, batch_idxs, acc) )
saver.save(sess, "./Model2/model.ckpt")
val, l = sess.run([img_test, label_test])
l = one_hot(l,6)
print(l)
y, acc = sess.run([y_conv, accuracy], feed_dict={x: val, y_: l})
print(y)
print("test accuracy: [%.8f]" % (acc))
coord.request_stop()
coord.join(threads)
Predict.py
import tensorflow as tf
import numpy as np
from PIL import Image
import os
import glob
path = './Dataset_Final_Enhanced/1/2.png'
precision = 1
np.set_printoptions(threshold=np.inf)
def get_image_paths(folder):
return glob.glob(os.path.join(folder, '*.png'))
#convolution layer
def conv2d(x,W):
return tf.nn.conv2d(x, W, strides=[1,1,1,1], padding='SAME')
#max_pool layer
def max_pool_4x4(x):
return tf.nn.max_pool(x, ksize=[1,4,4,1], strides=[1,4,4,1], padding='SAME')
sess = tf.Session()
# First let's load meta graph and restore weights
saver = tf.train.import_meta_graph('./Model/model.ckpt.meta')
# saver = tf.train.import_meta_graph('./Model2/model.ckpt.meta')
saver.restore(sess, './Model/model.ckpt')
# Access the graph
graph = tf.get_default_graph()
conv1_w = sess.run(graph.get_tensor_by_name('w1:0'))
conv1_b = sess.run(graph.get_tensor_by_name('b1:0'))
conv2_w = sess.run(graph.get_tensor_by_name('w2:0'))
conv2_b = sess.run(graph.get_tensor_by_name('b2:0'))
fc1_w = sess.run(graph.get_tensor_by_name('wfc1:0'))
fc1_b = sess.run(graph.get_tensor_by_name('bfc1:0'))
conv1_w_1 = np.around(conv1_w, decimals=precision)
conv1_b_1 = np.around(conv1_b, decimals=precision)
conv2_w_1 = np.around(conv2_w, decimals=precision)
conv2_b_1 = np.around(conv2_b, decimals=precision)
fc1_w_1 = np.around(fc1_w, decimals=precision)
fc1_b_1 = np.around(fc1_b, decimals=precision)
x = tf.placeholder(tf.float32, [1,64,64,1])
# first convolution and max_pool layer
W_conv1 = tf.Variable(tf.truncated_normal([3,3,1,4], stddev = 0.02), name="w1")
b_conv1 = tf.Variable(tf.constant(0.0, shape=[4]), name="b1")
h_conv1 = tf.nn.relu(conv2d(x, W_conv1) + b_conv1)
h_pool1 = max_pool_4x4(h_conv1)
# second convolution and max_pool layer
W_conv2 = tf.Variable(tf.truncated_normal([3,3,4,8], stddev = 0.02), name="w2")
b_conv2 = tf.Variable(tf.constant(0.0, shape=[8]), name="b2")
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
h_pool2 = max_pool_4x4(h_conv2)
# 变成全连接层,用一个MLP处理
reshape = tf.reshape(h_pool2,[1, -1])
W_fc1 = tf.Variable(tf.truncated_normal([128, 6], stddev = 0.02), name="wfc1")
b_fc1 = tf.Variable(tf.constant(0.0, shape=[6]), name="bfc1")
h_out = tf.matmul(reshape, W_fc1) + b_fc1
y_conv = tf.nn.softmax(tf.matmul(reshape, W_fc1) + b_fc1)
def predict(image_path):
image = Image.open(image_path).convert('L')
image_array = np.array(image)
image_array = image_array.astype(float)
x_input = np.reshape(image_array,[1,64,64,1]) * (1./255)
# h1 = sess.run(h_pool1, feed_dict={x:x_input,W_conv1:conv1_w_1, b_conv1:conv1_b_1})
# print(h1)
# h2 = sess.run(h_pool2, feed_dict={x:x_input,W_conv1:conv1_w_1, b_conv1:conv1_b_1, W_conv2:conv2_w_1, b_conv2:conv2_b_1})
# print(h2)
# print(image_path)
# h3 = sess.run(h_out,
# feed_dict={x: x_input, W_conv1: conv1_w_1, b_conv1: conv1_b_1, W_conv2: conv2_w_1, b_conv2: conv2_b_1,
# W_fc1: fc1_w_1, b_fc1: fc1_b_1})
# print(h3)
y = sess.run(y_conv, feed_dict={x:x_input,W_conv1:conv1_w_1, b_conv1:conv1_b_1, W_conv2:conv2_w_1, b_conv2:conv2_b_1, W_fc1:fc1_w_1, b_fc1:fc1_b_1})
# print(image_path)
# print(y)
y_out = np.array(y)
result = np.argmax(y_out)+1
return result
# print('predict result is',np.argmax(y_out)+1)
img_path = './Dataset_Final_Enhanced/test/1/'
imgs = get_image_paths(img_path)
sum = 0
correct = 0
for i in imgs:
sum = sum + 1
if(predict(i) == 1):
correct = correct + 1
# predict('./cnn_input.png')
# predict('./Dataset_Final_Enhanced/test/1/'+str(i)+'.png')
# predict('./Dataset_Final_Enhanced/test/4/' + str(i) + '.png')
print('accuracy is',correct/sum)
- 权重导出&&部署
这部分就不细谈了,毕竟和deep learning没关系。主要就是把浮点的权重导出然后用c++处理成了二进制文件,然后让fpga能够读入到加速器里面去。过程中主要是得理清楚权重文件里面每一个值对应的是哪个矩阵的哪个通道的哪一行哪一列上的元素。我们通过一个c++实现的bench去验证了这个顺序,确保结果和python算出来的是一致的,然后再verilog一五一十的对照着实现。
5. 总结
以上就是我的deep learing入门过程,后来的数个项目基本上就是重复这个flow。
总结:要用deep learning方法去解决一个任务:
- 有没有合适的开源的数据集?
- Yes --- 下下来,搞懂怎么用
- No --- 自己根据自己的需求去做
- 有没有现成的网络可以用?
- Yes --- 下下来,搞懂怎么用
- No --- 自己根据自己的需求去搭建/裁剪
- 网络训练不收敛怎么办?
- 检查代码
- 调整超参数
- 借助tensorboard之类的工具搞清楚问题
- 权重训练出来了怎么部署?
- 根据使用的硬件平台导出对应格式的权重/模型
- 使用工具/自己实现的代码进行部署
以上就是我的deep learning入坑过程。仅供参考,欢迎交流。
标签:fc1,Noob,img,Deep,conv2,conv1,train,Learning,tf From: https://www.cnblogs.com/sasasatori/p/16777797.html