(NN)深度神经网络物体识别或人脸识别

才接触的神经网络写了一个3层的神经网络进行人脸识别
程序如有不当之处请大家指出

Python实现

以下写了一个类实现了任意(n)分类;提供3个功能(测试数据需要自己提供)

  1. training()方法:神经网络的训练
  2. detection()方法:神经网络的检测
  3. 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

以下是使用上面类

  1. 卷积神经网络的训练
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()

  1. 卷积神经网络的测试
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()

  1. 开启摄像头使用模型检测(我这里做的是人脸检测)
from utilityClass.cnn_class import NN

nn = NN(None,  None,  None,  None,  None, "C:/Users/Andy/Desktop/NNmodel/")
nn.camera()
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值