在linux和windows系统底下构建和管理python虚拟环境
深度学习和Tensorflow中的概念和用法
用OpenCV和Tensorflow自己动手写一整个项目,包括数据收集,训练模型及部署
学会一种开发思维,而不是纯理论知识
你将收获
在linux和windows系统底下构建和管理python虚拟环境
深度学习和Tensorflow中的概念和用法
用OpenCV和Tensorflow自己动手写一整个项目,包括数据收集,训练模型及部署
学会一种开发思维,而不是纯理论知识
适用人群
课程介绍
课程目录
大家都在学
正在加载中...
同学笔记
2020-07-01 12:15:25
目录
import cv2
import tensorflow as tf
with tf.Session() as sess:
"""加载模型"""
loader = tf.train.import_meta_graph("./model/model.meta")
loader.restore(sess, './model/model')
"""开始识别"""
capture = cv2.VideoCapture(0)
while True:
ret, frame = capture.read() # 获取一帧
show_img = frame.copy() # show_img是原图像frame的拷贝
crop_img = frame[100:200, 100:200] # crop_img是在原图像frame上的部分截取
cv2.rectangle(show_img, (100, 100), (200, 200), (0, 0, 255), 5) # 在show_img上画出截取部分的框框
cv2.imshow('frame', show_img) # 显示图片show_img
k = cv2.waitKey(1) # OpenCV延迟1毫秒、同时检测是否有按键被按下(如果有键被按下时,将键值返回给q)
# 按下Q键退出
if k == ord('q'):
break
# 按下S键进行识别
elif k == ord('s'):
out = sess.run("output:0", feed_dict={"data_in:0": [crop_img]})
res = ['我', '好', '帅'][out[0]]
print("识别结果:", res)
cv2.imshow("crop", crop_img)
capture.release()
cv2.destroyAllWindows()
要有一个会话sess,要构建好模型载入器,要将模型文件路径填入,恢复到会话中
和准备数据集时同样的方法,使用cv调用摄像头,框选部分图像,作为输入
在会话sess中运行模型中与输出相应的"name:后缀",同时将测试图像的数据feed给模型中与输入相应的"name:后缀",得到的返回值即为输出结果。
这里需要注意的是,本质上sess读取的模型中就蕴含了原来训练时神经网络的结构,所以name要和原来训练时神经网络中变量的name相对应
API |
作用 |
使用示例 |
tf.train.import_meta_graph |
构建tf会话载入器 |
loader = tf.train.import_meta_graph("./model/model.meta") |
*.restore |
将训练好的模型文件加载到会话中 |
loader.restore(sess, './model/model') |
2020-07-01 11:08:39
目录
import tensorflow as tf
import cv2
import glob
import os
import numpy as np
def data_reader(dataset_path):
data_list = []
label_list = []
for cls_path in glob.glob(os.path.join(dataset_path, '*')):
for file_name in glob.glob((os.path.join(cls_path, '*'))):
img = cv2.imread(file_name)
data_list.append(img)
label_list.append(int(cls_path[-1]))
data_np = np.array(data_list)
label_np = np.array(label_list)
return data_np, label_np
def shuffle_data(data, label):
idx = np.arange(len(data))
np.random.shuffle(idx)
return data[idx, ...], label[idx, ...]
def train(data, label):
data_in = tf.placeholder(tf.float32, [None, 100, 100, 3], name="data_in") # None实际上指的是batch的大小,batch的大小可以在运行时改变
label_in = tf.placeholder(tf.float32, [None, 3]) # 准备构造一个one_hot的label,而我们的数据总共有3个类
# 一个小的卷积网络,用来处理图片
# (如果数据量大,需要构造一个大一点的神经网络,可以在卷积网络部分复制一下,就可以构造一个比较深的神经网络)
# (但是神经网络并不是越深越好,可能会涉及过拟合、梯度消失等问题)
# (目前网络参数的取值,主要还是根据数据集特征和大小,还有靠我们的直觉的经验。)
# (所以神经网络也叫作当代炼金术)
out = tf.layers.conv2d(data_in, 4, 3, padding='same') # out形状为[?, 100, 100, 4]
out = tf.layers.max_pooling2d(out, 2, 2, padding='same') # out形状为[?, 50, 50, 4]
out = tf.nn.relu(out) # out形状为[?, 50, 50, 4]
# 把提取出来的特征压扁成一个一维数组
out = tf.reshape(out, (-1, int(np.prod(out.get_shape()[1:])))) # out形状为[?, 10000]
# 送入全连接层
out = tf.layers.dense(out, 2000, activation=tf.nn.relu)
# 再加一个全连接层(按理说,我们数据量很少,应该会过拟合,也就数准确率接近100%)
out = tf.layers.dense(out, 256, activation=tf.nn.relu)
# 得到输出(输出的分类数要和输入的label的分类数一样,不然会报错)
pred = tf.layers.dense(out, 3)
# 将one_hot变回值0、1、2
out_label = tf.argmax(pred, 1, name="output")
# 计算交叉熵损失
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(labels=label_in, logits=pred))
# 梯度下降、优化损失
train_op = tf.train.AdamOptimizer(learning_rate=0.001).minimize(loss)
# 初始化变量的op
init_op = tf.initialize_all_variables()
# 定义一些参数
batch_size = 16
# 进行会话
with tf.Session() as sess:
sess.run(init_op)
# 进行多次训练
for epoch in range(50):
# (在训练的时候,我们不希望每次取到的数据的顺序都是一样的,这样很容易导致过拟合)
# (因此我们一般会加一个shuffle,也就是在输入到网络前,把数据顺序打乱)
datas, labels = shuffle_data(data, label)
# 准确率统计
total_loss = 0
avg_accuracy = 0
# 按批次训练
num_batch = len(data) // batch_size
for batch_idx in range(num_batch):
# 计算每个batch开始和结束时候的下标
start_idx = batch_idx * batch_size
end_idx = (batch_idx + 1) * batch_size
# 准备好输入层的数据
# (...代表省略后面的形状)
feed_dict = {data_in: datas[start_idx: end_idx, ...],
label_in: labels[start_idx: end_idx, ...]}
# 计算准确率
correct_prediction = tf.equal(out_label, tf.argmax(label_in, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
# 进行训练
_, loss_out, acc = sess.run([train_op, loss, accuracy], feed_dict=feed_dict)
# 准确率统计
total_loss += loss_out
avg_accuracy += acc
# 统计每批次准确率
print("avg_accuracy", avg_accuracy / num_batch)
# 准确率够高则保存模型
if avg_accuracy / num_batch > 0.94:
saver = tf.train.Saver()
saver.save(sess, './model/model')
break
def dense_to_one_hot(label, num_class):
num_label = label.shape[0]
index_offset = np.arange(num_label) * num_class
label_one_hot = np.zeros((num_label, num_class))
label_one_hot.flat[index_offset + label.ravel()] = 1
return label_one_hot
if __name__ == '__main__':
dataset_path = "./dataset"
data, label = data_reader(dataset_path)
one_hot_label = dense_to_one_hot(label, 3)
train(data, one_hot_label)
图片数据
首先,用cv读取图片,将图片一个一个添加到一个list里面去。
其次,用np(numpy)将图片的列表转化为np数组。
图片目标值
首先,由于图片被按照目录分类
在读取图片时,每读取一张图,就将图片的上级目录的名字作为一个目标值。(这里是0、1、2)
步骤如下:
原本一个样本只有一个目标值
一共600个样本,目标值总的可能性有3种(某种目标的one_hot编码就只可能是[1, 0, 0]、[0, 1, 0]、[0, 0, 1])
先生成好一个全零的、形状为(600, 3)的np数组𝐴,准备将值1填入对应标签位置的位置
准备好每个样本在𝐴展平后的数组𝐵中的one_hot特征下标
将1填入上一步中展平下标对应在数组𝐴中的位置
可以通过一个占位符来实现,个数的那一维填写None,代表暂时不知道每次训练的样本数量,在实际训练中会根据每批次训练数量加以给定。
比如,我们这里的样本数为600,每个样本的形状都是(100, 100, 3),也就是总的样本的形状为(600, 100, 100, 3),那么我们样本输入的占位符的形状就应该为(None, 100, 100, 3);同样地,我们one_hot目标值的形状为(600, 3),那么我们样本目标值的占位符形状就应该为(None, 3)
步骤如下:
接入一个卷积层(tf.layers.conv2d)
第一个参数(inputs)为输入数据
第二个参数(filters)为卷积核的数量
第三个参数(kernel_size)为卷积核的大小
(这里因为我们的数据比较简单、类别也少,所以filters取了比较小的值4)
(如果像是imagenet那种大型数据集一般会取到128或者256那样)
接入一个池化层(用于减少数据量)(tf.layers.max_pooling2d)
第一个参数(inputs)为输入
第二个参数(pool_size)为池化核的大小
第三个参数(strides)为卷积核的步长
(这里我们都把pool_size和strides设置为2)
加上一个激活函数ReLU
把神经网络的输出拍扁,做成一个一维数组
添加几个全连接层(tf.layers.dense)
第一个参数(inputs)为输入
第二个参数(unit)为输出的维度大小(改变inputs的最后一维),同时也是神经元的个数
(一开始中间加入的是一层,后来发现一层全连接层训练的效果不怎么好,改成了两层)
再接入一个全连接层得到输出
(注意,输出的分类数要和目标label的分类数相同,否则会报错)
损失计算
计算交叉熵损失
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(labels=label_in, logits=pred))
优化
使用Adam优化算法
train_op = tf.train.AdamOptimizer(learning_rate=0.001).minimize(loss)
步骤如下:
设置每批次的训练数量batch_size、训练的趟数
每趟训练前件训练用的输入数据随机打乱
计算好每一趟训练的批次数量,对每一批次用batch_size个样本进行训练
步骤如下:
找到预测值、目标值的每一个样本在one_hot编码中的横向下标
tf.argmax(pred, 1, name="output")
tf.argmax(label_in, 1)
对比预测值、目标值的下标值是否相等,由此得到一个判断对应样本的预测值与目标值是否相等的tf张量
correct_prediction = tf.equal(tf.argmax(pred, 1), tf.argmax(label_in, 1))
从上面的比对张量中计算平均值,得到这一批次的准确率
在一趟训练结束后,将所有批次准确率相加求和,再除以训练批次数量,就得到了这一趟训练的平均准确率
步骤如下:
构造一个tf模型存储器
saver = tf.train.Saver()
指定会话、模型存储路径
saver.save(sess, './目录/模型名')
这里需要注意的是,我们需要为输入数据占位符data_in、预测值out_label增加上各自的name,这是为了之后模型读取的方便
API |
作用 |
使用示例 |
glob.glob |
返回某个路径下所有被匹配的文件/目录路径生成的列表 |
for cls_path in glob.glob(os.path.join(dataset_path, '*')): ...... |
np.array |
根据某种数据类型的值生成相应的np数组 |
data_np = np.array(data_list) |
np.arange |
按照指定的start、stop、step生成一个指定的np一维等差数组 |
index_offset = np.arange(num_label) * num_class |
np.zeros |
按照指定的shape生成元素全为0的np数组 |
label_one_hot = np.zeros((num_label, num_class)) |
*.flat[???] |
根据数组展平后的下标获取数组元素 |
label_one_hot.flat[index_offset + label.ravel()] = 1 |
np.random.shuffle |
随机打乱一串数 |
np.random.shuffle(idx) |
tf.placeholder |
创建一个tf占位符 |
label_in = tf.placeholder(tf.float32, [None, 3]) |
tf.Session |
创建一个tf会话 |
with tf.Session() as sess: ...... |
tf.initialize_all_variables |
创建一个tf变量初始化op |
init_op = tf.initialize_all_variables() |
tf.train.Saver |
创建一个tf会话存储器 |
saver = tf.train.Saver() |
*.save |
保存会话中的训练模型 |
saver.save(sess, './model/model') |
tf.argmax |
找出张量在某一维度上的最大值对应的下标 |
out_label = tf.argmax(pred, 1, name="output") |
tf.reduce_mean |
计算平均值 |
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) |
tf.cast |
将某个张量的数据类型进行转换 |
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) |
tf.equal |
按元素比较两个张量,返回相等情况对应的张量 |
correct_prediction = tf.equal(out_label, tf.argmax(label_in, 1)) |
tf.nn. softmax_cross_entropy _with_logits_v2 |
计算交叉熵损失 |
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(labels=label_in, logits=pred)) |
tf.layers.conv2d |
构建卷积层 |
out = tf.layers.conv2d(data_in, 4, 3, padding='same') |
tf.layers.max_pooling2d |
构建池化层 |
out = tf.layers.max_pooling2d(out, 2, 2, padding='same') |
tf.nn.relu |
激活函数ReLU |
out = tf.nn.relu(out) |
tf.layers.dense |
构造全连接层 |
out = tf.layers.dense(out, 2000, activation=tf.nn.relu) |
2020-06-30 20:10:55
目录
import cv2
import os
"""数据集目录准备"""
dataset_path = "./dataset" # 数据集目录
# 检验数据集目录是否存在,不存在则创建目录
if not os.path.exists(dataset_path):
os.mkdir(dataset_path)
# 在数据集路径下,检测3个类的路径是否存在,不存在则创建目录
for i in range(3):
class_path = dataset_path + f"/{i}"
if not os.path.exists(class_path):
os.mkdir(class_path)
"""图片编号准备"""
data_counter = 0 # 每一类图片的计数器
class_number = 0 # 图片类型编号
"""摄像头取材准备"""
capture = cv2.VideoCapture(0)
while True:
ret, frame = capture.read() # 获取一帧
show_img = frame.copy() # show_img是原图像frame的拷贝
crop_img = frame[100:200, 100:200] # crop_img是在原图像frame上的部分截取
cv2.rectangle(show_img, (100, 100), (200, 200), (0, 0, 255), 5) # 在show_img上画出截取部分的框框
cv2.imshow('frame', show_img) # 显示图片show_img
k = cv2.waitKey(1) # OpenCV延迟1毫秒、同时检测是否有按键被按下(如果有键被按下时,将键值返回给q)
# 按下Q键退出
if k == ord('q'):
break
# 按下S键保存
elif k == ord('s'):
file_name = dataset_path + f"/{class_number}" + "/%03d.jpg" % data_counter # 记录保存图片的相对路径
cv2.imwrite(file_name, crop_img) # 保存图片crop_img
print(f"保存 图片{data_counter}") # 打印提示
data_counter += 1 # 计数器 + 1
# 按下N键进入下一类样本的录入
elif k == ord('n'):
class_number += 1
data_counter = 0
print(f"进入第{class_number}类样本的识别")
capture.release()
cv2.destroyAllWindows()
主要靠os来判断文件夹是否存在,不存在将提前创建相应文件夹
主要靠OpenCV打开摄像头,截取框选区域图片进行保存
主要靠OpenCV获取按键,判别按键信息执行对应指令
在一个dataset的文件夹中,有三个文件夹【0、1、2】,分别对应三类图片数据【“我”、“好”、“帅”】,每一类图片存放在相应类型的文件夹下,各存储200张,编号均为000.jpg~199.jpg
API |
作用 |
使用示例 |
os.path.exists |
os判断某个文件/目录是否存在 |
dataset_path = "./dataset" if not os.path.exists(dataset_path): ...... |
os.mkdir |
os创建某个目录 |
os.mkdir(dataset_path) |
cv2.VideoCapture |
cv获取摄像头资源 |
capture = cv2.VideoCapture(0) |
*.read |
cv获取摄像头的一帧 |
ret, frame = capture.read() |
*.copy |
cv图片拷贝 |
show_img = frame.copy() |
*[???:???, ???:???] |
cv图片截取 |
crop_img = frame[100:200, 100:200] |
cv2.rectangle |
cv图片画框 |
cv2.rectangle(show_img, (100, 100), (200, 200), (0, 0, 255), 5) |
cv2.imshow |
cv图片展示 |
cv2.imshow('frame', show_img) |
cv2.imwrite |
cv图片保存 |
cv2.imwrite(file_name, crop_img) |
cv2.waitKey |
cv等待按键 |
k = cv2.waitKey(1) |
*.release |
cv释放摄像头资源 |
capture.release() |
cv2.destroyAllWindows |
关闭所有cv窗口 |
cv2.destroyAllWindows() |
ord |
返回字符对应的ASCII码值 |
if k == ord('q'): break |
没有更多了