才接触的神经网络写了一个3层的神经网络进行人脸识别
程序如有不当之处请大家指出
Python实现
以下写了一个类实现了任意(n)分类;提供3个功能(测试数据需要自己提供)
- training()方法:神经网络的训练
- detection()方法:神经网络的检测
- camera()方法:开启摄像头使用模型进行人脸分类
from PIL import Image, ImageDraw, ImageFont
import scipy.special
import traceback
import numpy
import csv
import sys
import cv2
import os
class NN:
def __init__(self, input_nodes=None,
hidden_nodes=None,
output_nodes=None,
learning_rate=None,
epochs=None,
save_path="./NNmodel/"):
self.input_nodes = input_nodes # 输入层节点数
self.hidden_nodes = hidden_nodes # 隐藏层节点数
self.output_nodes = output_nodes # 输出层节点数
self.learning_rate = learning_rate # 学习率
self.epochs = epochs # 迭代次数
self.save_path = save_path # 模型保存路径
self.data = [] # 训练数据
self.wih = [] # 输入层和隐藏层权重矩阵
self.who = [] # 隐藏层和输出层权重矩阵
self.scorecard = [] # 计分
# 激活函数是sigmoid函数;Sigmoid函数常被用作神经网络的激活函数
self.activation_function = lambda x: scipy.special.expit(x)
# 创建文件夹
self.create_folder()
pass
"""创建文件夹"""
def create_folder(self):
if not os.path.exists(self.save_path):
os.makedirs(self.save_path)
pass
"""返回字符串最后一次出现的下标"""
def find_last(self, strings, sting):
last_position = -1
while True:
position = strings.find(sting, last_position + 1)
if position == -1:
return last_position
last_position = position
pass
"""检查初始化"""
def check(self, path=None, index=None):
name = traceback.extract_stack()
if '%s' % name[-2][2] == "set_data":
if self.input_nodes is None or self.hidden_nodes is None or self.output_nodes is None:
print("\033[0;31m请按照以下格式设置初始化")
print("对象 = NN(输入层节点数, 隐藏层节点数, 输出层节点数)")
print("例:nn = NN(784, 200, 3)\033[0m")
sys.exit(0)
if self.input_nodes <= 0 or str(self.input_nodes).find(".") != -1:
print("\033[0;31m输入层节点数为:" + str(self.input_nodes))
print("输入层节点数只能是大于0的正整数,请重新设置输入层节点数\033[0m")
sys.exit(0)
if self.hidden_nodes < 0 or str(self.hidden_nodes).find(".") != -1:
print("\033[0;31m隐藏层节点数为:" + str(self.hidden_nodes))
print("隐藏层节点数只能是大于0的正整数,请重新设置隐藏层节点数\033[0m")
sys.exit(0)
if self.output_nodes <= 0 or str(self.output_nodes).find(".") != -1:
print("\033[0;31m输出层节点数为:" + str(self.output_nodes))
print("输出层节点数只能是大于0的正整数,请重新设置输出层节点数\033[0m")
sys.exit(0)
if int(numpy.sqrt(self.input_nodes)) * int(numpy.sqrt(self.input_nodes)) != self.input_nodes:
print("\033[0;31m输入层节点数为:" + str(self.input_nodes))
print("不适合网络设置,请重新设置输入层节点数\033[0m")
sys.exit(0)
pass
if path is None or index is None:
print("\033[0;31m请按照以下格式设置数据")
print("对象.set_data(图片路径, 分类标签有效下标)")
print("例:nn.set_data(\"C:/Users/Andy/Desktop/test\", 0)\033[0m")
sys.exit(0)
if not os.path.exists(path):
print("\033[0;31m对象.set_data(\"" + path + "\", " + str(index) + ")")
print("指定的数据源:" + path + "不存在\033[0m")
sys.exit(0)
if index < 0 or str(index).find(".") != -1:
print("\033[0;31m分类标签有效下标为:" + str(index))
print("分类标签有效下标必须是大于等于0的正整数\033[0m")
sys.exit(0)
if index > self.output_nodes - 1:
print("\033[0;31m对象.set_data(\"" + path + "\", " + str(index) + ")")
print("设置的分类标签有效下标:" + str(index))
print("对象 = NN(" + str(self.input_nodes) + ", " + str(self.hidden_nodes) + ", " + str(self.output_nodes)
+ ", " + str(self.learning_rate) + ", " + str(self.epochs) + ")")
print("初始化输出节点是" + str(self.output_nodes) + "转换成分类标签有效最大下标为:" + str(self.output_nodes-1))
print("设置的分类标签有效下标大于初始化的输出节点转换成分类标签有效最大下标(下标从0开始)\033[0m")
sys.exit(0)
if '%s' % name[-2][2] == "training":
if self.learning_rate is None or self.epochs is None:
print("\033[0;31m请按照以下格式设置初始化")
print("对象= NN(输入层节点数, 隐藏层节点数, 输出层节点数, 学习率, 世代)")
print("例:nn = NN(784, 200, 3, 0.2, 5)\033[0m")
sys.exit(0)
if len(self.data) < 1:
print("\033[0;31m请先使用如下方法设置数据集再使用training()方法")
print("对象.set_data(图片路径,标签下标)")
print("例:nn.set_data(\"C:/Users/Andy/Desktop/test\", 0)\033[0m")
sys.exit(0)
if self.learning_rate <= 0:
print("\033[0;31m学习率为:" + str(self.learning_rate))
print("学习率只能是大于0的正数,请重新设置学习率\033[0m")
sys.exit(0)
if self.epochs <= 0 or str(self.epochs).find(".") != -1:
print("\033[0;31m训练世代为:" + str(self.epochs))
print("训练世代只能是大于0的正整数,请重新设置训练世代\033[0m")
sys.exit(0)
if os.path.exists(self.save_path + "wih.csv") and os.path.exists(self.save_path + "who.csv"):
print("\033[0;31m默认路径:" + self.save_path + "中已经存在模型文件")
print("Y覆盖模型文件 N终止程序")
text = input("是否覆盖原有的模型文件?\033[0m")
if text == "Y":
pass
else:
sys.exit(0)
if '%s' % name[-2][2] == "detection":
if os.path.exists(self.save_path + "wih.csv") and os.path.exists(self.save_path + "who.csv"):
self.get_model()
else:
print("\033[0;31m默认的路径下没有找到模型文件请按照以下方式设置")
print("对象=NN(输入层节点数, 隐藏层节点数, 输出层节点数, None, None, 模型保存路径)")
print("例:nn = NN(784, 200, 3, None, None, \"C:/Users/Andy/Desktop/NNmodel/\")\033[0m")
sys.exit(0)
if len(self.data) < 1:
print("\033[0;31m请先使用如下方法设置数据集再使用detection()方法")
print("对象.set_data(图片路径,分类标签有效下标)")
print("例:nn.set_data(\"C:/Users/Andy/Desktop/test\", 0)\033[0m")
sys.exit(0)
if '%s' % name[-2][2] == "camera":
if os.path.exists(self.save_path + "wih.csv") and os.path.exists(self.save_path + "who.csv"):
self.get_model()
else:
print("\033[0;31m默认路径" + self.save_path+"下没有找到模型文件!请按如下设置模型文件路径")
print("对象=NN(None, None, None, None, None, 模型保存路径)")
print("例:nn = NN(None, None, None, None, None, \"C:/Users/Andy/Desktop/NNmodel/\")\033[0m")
sys.exit(0)
pass
"""
输入层和隐藏层权重矩阵
隐藏层和输出层权重矩阵
"""
def init_weight_matrix(self):
# 输入层和隐藏层节点间链路权重形成的矩阵大小(隐藏层的节点数*输入层的节点数)
self.wih = numpy.random.normal(0.0, pow(self.hidden_nodes, -0.5), (self.hidden_nodes, self.input_nodes))
# 隐藏层和输出层间链路权重形成的矩阵大小(输出层节点数*隐藏层节点数)
self.who = numpy.random.normal(0.0, pow(self.output_nodes, -0.5), (self.output_nodes, self.hidden_nodes))
pass
"""标签转换"""
def format_label(self, label):
new_label = numpy.zeros(len(label)) + 0.01 # 所有目标输出值设置为0.01
index = numpy.argmax(label) # 正确目标标签的序号
new_label[index] = 0.99 # 正确目标标签设置为0.99
return new_label
pass
"""取得数据"""
def set_data(self, path, index):
self.check(path, index)
print("正在读取" + path + "中的数据...")
for file in os.listdir(path):
if file.endswith('.jpg') or file.endswith('.png'):
temp = []
file = path + '/' + file
image = cv2.imread(file)
top, bottom, left, right = self.padding_size(image)
image = cv2.copyMakeBorder(image, top, bottom, left, right, cv2.BORDER_CONSTANT, value=0)
image = cv2.resize(image, self.image_size(), interpolation=cv2.INTER_AREA) # 图像缩放到指定大小
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 图像转灰度图
gray_image = numpy.reshape(gray_image, (1, -1))
gray_image = (gray_image / 255.0 * 0.99) + 0.01 # 数据缩放到0.01到1.00
temp.append(self.set_label(index)) # label加入temp
temp.append(gray_image) # gray_image加入temp
self.data.append(temp) # temp加入input_data列表中
print(path + "中的数据加载成功...")
pass
"""按长宽中的最大值获得上下左右需要填充的大小"""
def padding_size(self, image):
height, width = image.shape[:2] # 同时给长宽赋不同值
top, bottom, left, right = (0, 0, 0, 0) # 同时给上下左右赋不同值
longest = max(height, width)
if width < longest:
tmp = longest - width
left = tmp // 2
right = tmp - left
if height < longest:
tmp = longest - height
top = tmp // 2
bottom = tmp - top
return top, bottom, left, right
pass
"""根据下标设置标签"""
def set_label(self, index):
label = []
for i in range(self.output_nodes):
if i == index:
label.append(1)
else:
label.append(0)
return label
pass
"""在图片上输入中文"""
def image_draw_chinese(self, image, text, location, text_color, text_size):
cv2img = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # 转化为PIL库的格式
pilimg = Image.fromarray(cv2img)
draw = ImageDraw.Draw(pilimg) # draw对象
font_style = ImageFont.truetype("font/simsun.ttc", text_size, encoding="utf-8")
draw.text(location, text, text_color, font=font_style) # 绘制文本
return cv2.cvtColor(numpy.asarray(pilimg), cv2.COLOR_RGB2BGR) # PIL转cv2 图片
pass
"""打乱数据集"""
def shuffle_data(self, data):
data = numpy.array(data)
data_num, _ = data.shape # 得到样本数
index = numpy.arange(data_num) # 生成下标
numpy.random.shuffle(index) # 打乱下标
return data[index]
pass
"""取得图片矩阵大小"""
def image_size(self):
size = (int(numpy.sqrt(self.input_nodes)), int(numpy.sqrt(self.input_nodes)))
return size
pass
"""取得模型的输入矩阵大小"""
def get_model_size(self):
file = open(self.save_path + "/wih.csv")
data = csv.reader(file)
data = list(data)
column = len(data[0])
file.close()
size = (int(numpy.sqrt(column)), int(numpy.sqrt(column)))
return size
pass
"""保存模型"""
def save_model(self):
numpy.savetxt(self.save_path + "wih.csv", self.wih, delimiter=',')
numpy.savetxt(self.save_path + "who.csv", self.who, delimiter=',')
print("模型已保存!")
pass
"""使用模型"""
def get_model(self):
self.wih = numpy.loadtxt(open(self.save_path + "wih.csv", "rb"), delimiter=",")
self.wih = numpy.array(self.wih, ndmin=2)
self.who = numpy.loadtxt(open(self.save_path + "who.csv", "rb"), delimiter=",")
self.who = numpy.array(self.who, ndmin=2)
pass
"""训练神经网络"""
def training(self):
self.check()
self.init_weight_matrix()
self.data = self.shuffle_data(self.data)
scorecard = []
for i in range(self.epochs):
for j in range(len(self.data)):
label = self.data[j][0]
data = self.data[j][1]
# 输入数据正向传递
data = numpy.array(data, ndmin=2).T # 输入数据转换成二维矩阵.T表示做矩阵的转置
label = numpy.array(label, ndmin=2).T # 目标数据转换成二维矩阵.T表示做矩阵的转置
hidden_input = numpy.dot(self.wih, data) # 计算输入层到隐藏层的信号
hidden_output = self.activation_function(hidden_input) # 隐藏层信号应用激活函数后形成的信号
output_input = numpy.dot(self.who, hidden_output) # 计算进入输出层的信号
output_output = self.activation_function(output_input) # 输出层信号应用激活函数后形成最终的输出信号
# 计算差值
output_error = label - output_output # 预期输出-实际输出
#print(output_error)
hidden_errors = numpy.dot(self.who.T, output_error) # 输入层和隐藏层误差(按照输出权重矩阵分割误差)
#print("-------------")
# 更新隐藏层和输出层之间链接的权重
self.who += self.learning_rate * numpy.dot((output_error * output_output * (1.0 - output_output)),
numpy.transpose(hidden_output))
# 更新输入层和隐藏层之间链接的权重
self.wih += self.learning_rate * numpy.dot((hidden_errors * hidden_output * (1.0 - hidden_output)),
numpy.transpose(data))
# 添加正确或不正确的列表
if numpy.argmax(label) == numpy.argmax(output_output):
scorecard.append(1)
else:
scorecard.append(0)
# 计算成绩,正确答案的分数
scorecard_array = numpy.asarray(scorecard)
print("世代" + str(i+1) + "正确率 = ", scorecard_array.sum() / scorecard_array.size)
print(str(self.epochs * len(self.data)) + "个数据已训练完成!")
self.save_model()
pass
"""使用模型检测"""
def detection(self):
self.check()
self.data = self.shuffle_data(self.data)
scorecard = []
for i in range(len(self.data)):
label = self.data[i][0]
data = self.data[i][1]
# 输入数据正向传递
data = numpy.array(data, ndmin=2).T # 输入数据转换成二维矩阵.T表示做矩阵的转置
label = numpy.array(label, ndmin=2).T # 目标数据转换成二维矩阵.T表示做矩阵的转置
hidden_input = numpy.dot(self.wih, data) # 计算输入层到隐藏层的信号
hidden_output = self.activation_function(hidden_input) # 隐藏层信号应用激活函数后形成的信号
output_input = numpy.dot(self.who, hidden_output) # 计算进入输出层的信号
output_output = self.activation_function(output_input) # 输出层信号应用激活函数后形成最终的输出信号
# 添加正确或不正确的列表
if numpy.argmax(label) == numpy.argmax(output_output):
scorecard.append(1)
else:
scorecard.append(0)
# 计算成绩,正确答案的分数
scorecard_array = numpy.asarray(scorecard)
print("使用模型检测的正确率 = ", scorecard_array.sum() / scorecard_array.size)
pass
"""开启摄像头使用模型检测"""
def camera(self):
self.check()
frame_color = (255, 0, 0) # 边框颜色
text_color = (255, 0, 0) # 字体颜色
# Opencv自带的人脸检测级联分类器 (在Opencv安装目录下的sources/data/haarcascades/haarcascade_frontalface_default.xml)
detector = cv2.CascadeClassifier('D:/opencv/sources/data/haarcascades/haarcascade_frontalface_default.xml')
cap = cv2.VideoCapture(0)
while True:
ret, frame = cap.read() # 获取一帧
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 将这帧转换为灰度图
face = detector.detectMultiScale(gray, 1.3, 5)
if len(face) > 0:
for faceRect in face:
x, y, w, h = faceRect
cv2.rectangle(frame, (x, y), (x + w, y + h), frame_color, 2)
face = frame[x:x+w, y:y+h]
face = cv2.resize(face, self.get_model_size(), interpolation=cv2.INTER_AREA) # 图像缩放到指定大小
face = cv2.cvtColor(face, cv2.COLOR_BGR2GRAY) # 图像转灰度图
face = numpy.reshape(face, (1, -1))
face = (face / 255.0 * 0.99) + 0.01 # 数据缩放到0.01到1.00
data = numpy.array(face, ndmin=2).T # 输入数据转换成二维矩阵.T表示做矩阵的转置
hidden_input = numpy.dot(self.wih, data) # 计算输入层到隐藏层的信号
hidden_output = self.activation_function(hidden_input) # 隐藏层信号应用激活函数后形成的信号
output_input = numpy.dot(self.who, hidden_output) # 计算进入输出层的信号
output_output = self.activation_function(output_input) # 输出层信号应用激活函数后形成最终的输出信号
if numpy.argmax(output_output) == 0:
frame = self.image_draw_chinese(frame, "英芷菡", (x, y), text_color, 20)
if numpy.argmax(output_output) == 1:
frame = self.image_draw_chinese(frame, "英伟", (x, y), text_color, 20)
if numpy.argmax(output_output) == 2:
frame = self.image_draw_chinese(frame, "英鑫", (x, y), text_color, 20)
cv2.imshow("img", frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
pass
以下是使用上面类
- 卷积神经网络的训练
from utilityClass.cnn_class import NN
import cv2
nn = NN(784, 200, 3, 0.2, 5, "C:/Users/Andy/Desktop/NNmodel/")
nn.set_data("C:/Users/Andy/Desktop/aa", 0)
nn.set_data("C:/Users/Andy/Desktop/bb", 1)
nn.set_data("C:/Users/Andy/Desktop/cc", 2)
nn.training()
- 卷积神经网络的测试
from utilityClass.cnn_class import NN
import cv2
nn = NN(784, 200, 3, 0.2, 5, "C:/Users/Andy/Desktop/NNmodel/")
nn.set_data("C:/Users/Andy/Desktop/aa", 0)
nn.set_data("C:/Users/Andy/Desktop/bb", 1)
nn.set_data("C:/Users/Andy/Desktop/cc", 2)
nn.detection()
- 开启摄像头使用模型检测(我这里做的是人脸检测)
from utilityClass.cnn_class import NN
nn = NN(None, None, None, None, None, "C:/Users/Andy/Desktop/NNmodel/")
nn.camera()