python ocr(光学文字识别)学习笔记 (二)

参考资料:500 lines or less ocr 其中包括神经网络算法的简单介绍,如果看不懂您需要使用谷歌翻译呢

原博客链接:http://www.cnblogs.com/bitch1319453/

在这一节内容中,我们将对实现这个系统的算法进行分析

设计feedforward ANN(前馈神经网络,也称bp神经网络)时,我们需要考虑以下因素:

1.激活函数的选用

激活函数是结点输出的决策者。我们这个系统将为每个数字输出一个介于0到1的值,值越接近1意味着ann预测的是绘制的数字,越接近0意味着它被预测不是绘制的数字。因此我们将输出接近0或者1的激活函数。我们还需要一个可微分的函数。这是由于我们的bp神经网络基于万能逼近定理:对于一个任意闭区间的连续函数,都可以用隐含层的bp网络来逼近。所以一个3层的bp网络可以完成任意m维到n维的映射。在反向传播计算的时候需要求导所以要求函数是可微的

所以我们可以参看 常用激活函数列表

最终选择了Logistic,也就是s型激活函数了

2. biases(偏移因子,又译成阀值)

使用起到加快收敛的作用。收敛次数可以相当于神经网络进行正确的判断的训练次数,用数学方法使得神经网络更“聪明”

3.神经网络隐藏层的数量和隐藏层节点的数量

在多数的情况下,单个隐藏层是足够的,这里使用neural_network_design.pyl来对隐藏层节点数量进行测试

这里导入的ocr.py的类

"""
In order to decide how many hidden nodes the hidden layer should have,
split up the data set into training and testing data and create networks
with various hidden node counts (5, 10, 15, ... 45), testing the performance
for each.

The best-performing node count is used in the actual system. If multiple counts
perform similarly, choose the smallest count for a smaller network with fewer computations.
"""

import numpy as np
from ocr import OCRNeuralNetwork
from sklearn.cross_validation import train_test_split

def test(data_matrix, data_labels, test_indices, nn):
    avg_sum = 0
    for j in xrange(100):
        correct_guess_count = 0
        for i in test_indices:
            test = data_matrix[i]
            prediction = nn.predict(test)
            if data_labels[i] == prediction:
                correct_guess_count += 1

        avg_sum += (correct_guess_count / float(len(test_indices)))
    return avg_sum / 100


# Load data samples and labels into matrix
data_matrix = np.loadtxt(open('data.csv', 'rb'), delimiter = ',').tolist()
data_labels = np.loadtxt(open('dataLabels.csv', 'rb')).tolist()

# Create training and testing sets.
train_indices, test_indices = train_test_split(list(range(5000)))

print "PERFORMANCE"
print "-----------"

# Try various number of hidden nodes and see what performs best
for i in xrange(5, 50, 5):
    nn = OCRNeuralNetwork(i, data_matrix, data_labels, train_indices, False)
    performance = str(test(data_matrix, data_labels, test_indices, nn))
    print "{i} Hidden Nodes: {val}".format(i=i, val=performance)

 之后是在ocr.py中构建主类,包含反向传播训练,网络预测等方法

我们使用反向传播算法训练我们的ANN。它由训练集中的每个样本重复的4个主要步骤来更新ANN权重。

1.初始化数据

首先,我们将权重初始化为小(在-1和1之间)随机值。在我们的例子中,我们将它们初始化为-0.06和0.06之间的值,并将其存储在矩阵theta1theta2input_layer_bias,和hidden_layer_bias由于层中的每个节点链接到下一层的每个节点,我们可以创建一个具有m行n列的矩阵,其中n是一层中的节点数,m是相邻层中的节点数。该矩阵将表示这两个层之间的链接的所有权重。这里,theta1具有400列,用于我们的20x20像素输入和num_hidden_nodes行。同样,theta2表示隐藏层和输出层之间的链接。它有num_hidden_nodes列和NUM_DIGITS10)行。其他两个向量(1行),input_layer_bias和hidden_layer_bias表示偏移因子。

2.forward propagation(前向传播)

 第二步是前向传播,其本质上是如[什么是anns]中所描述的那样从输入节点开始逐层地计算的节点输出。这里,`y0`是我们希望用来训练ANN的大小为400的数组输入。我们将theta1乘以`y0`的转置矩阵,使得我们有两个大小为(`num_hidden_​​nodes×400)*(400×1)`的矩阵,并且具有对于大小为`num_hidden_​​nodes`的隐藏层的输出的结果向量。然后,我们添加偏移因子,并应用矢量化S形激活函数得到一个输出向量`y1`。 `y1`是我们隐藏层的输出向量。再次重复相同的过程以计算输出节点的`y2`。 `y2`现在是我们的输出层向量,其值表示它们的索引是绘制数字的可能性。例如,如果有人绘制一个8,如果ANN做出正确的预测,则在第8个索引处的`y2`的值将是最大的。然而,6可能具有比为所绘制的数字的1更高的似然性,因为其看起来更类似于8,并且和8也有着更多重叠得像素.`y2`随着很多用于训练的绘制的数字,ANN将会变得更准确。

3.back propagation

 第三步是反向传播,其涉及计算输出节点处的错误,然后在每个中间层返回到输入。这里我们首先创建一个期望的输出向量`actual_vals`,在表示绘制数字的值的数字的索引为1,否则为0。输出节点处的误差向量`output_errors`通过从`actual_vals`中减去实际输出向量`y2`来计算。对于每个隐藏层之后,我们计算两个组件。首先,我们有下一层的转置权重矩阵乘以其输出误差。然后我们得到应用于上一层的激活函数的导数。然后,我们对这两个分量执行元素级乘法,得到隐藏层的误差向量。这里我们称之为`hidden_​​errors`。

4.基于先前计算的误差得到的权重更新,调整ANN权重。

通过矩阵乘法在每一层更新权重。每层的误差矩阵乘以前一层的输出矩阵。然后将该乘积乘以称为学习速率的标量,并将其加到权重矩阵。学习速率是在0和1之间的值,其影响ANN中的学习的速度和准确性。较大的学习速率值将生成快速学习但不太准确的ANN,而较小的值将生成学习速度较慢但是更准确的ANN。在我们的例子中,我们有一个相对较小的学习率,0.1。因为我们没有为了使用户进行训练或预测请求而立即完成对ANN的训练的需求,所以这样的学习率很不错。这样我们就可以通过简单地将学习速率乘以层的误差向量来更新偏差。

python源码如下,使用了python的科学计算包创建矩阵,进行矩阵运算。而激活函数的导数手动求好直接调用

import csv
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import numpy as np
from numpy import matrix
from math import pow
from collections import namedtuple
import math
import random
import os
import json

"""
This class does some initial training of a neural network for predicting drawn
digits based on a data set in data_matrix and data_labels. It can then be used to
train the network further by calling train() with any array of data or to predict
what a drawn digit is by calling predict().

The weights that define the neural network can be saved to a file, NN_FILE_PATH,
to be reloaded upon initilization.
"""
class OCRNeuralNetwork:
    LEARNING_RATE = 0.1
    WIDTH_IN_PIXELS = 20
    NN_FILE_PATH = 'nn.json'

    def __init__(self, num_hidden_nodes, data_matrix, data_labels, training_indices, use_file=True):
        self.sigmoid = np.vectorize(self._sigmoid_scalar)
        self.sigmoid_prime = np.vectorize(self._sigmoid_prime_scalar)
        self._use_file = use_file
        self.data_matrix = data_matrix
        self.data_labels = data_labels

        if (not os.path.isfile(OCRNeuralNetwork.NN_FILE_PATH) or not use_file):
            # Step 1: Initialize weights to small numbers
            self.theta1 = self._rand_initialize_weights(400, num_hidden_nodes)
            self.theta2 = self._rand_initialize_weights(num_hidden_nodes, 10)
            self.input_layer_bias = self._rand_initialize_weights(1, num_hidden_nodes)
            self.hidden_layer_bias = self._rand_initialize_weights(1, 10)

            # Train using sample data
            TrainData = namedtuple('TrainData', ['y0', 'label'])
            self.train([TrainData(self.data_matrix[i], int(self.data_labels[i])) for i in training_indices])
            self.save()
        else:
            self._load()

    def _rand_initialize_weights(self, size_in, size_out):
        return [((x * 0.12) - 0.06) for x in np.random.rand(size_out, size_in)]

    # The sigmoid activation function. Operates on scalars.
    def _sigmoid_scalar(self, z):
        return 1 / (1 + math.e ** -z)

    def _sigmoid_prime_scalar(self, z):
        return self.sigmoid(z) * (1 - self.sigmoid(z))

    def _draw(self, sample):
        pixelArray = [sample[j:j+self.WIDTH_IN_PIXELS] for j in xrange(0, len(sample), self.WIDTH_IN_PIXELS)]
        plt.imshow(zip(*pixelArray), cmap = cm.Greys_r, interpolation="nearest")
        plt.show()

    def train(self, training_data_array):
        for data in training_data_array:
            # Step 2: Forward propagation
            y1 = np.dot(np.mat(self.theta1), np.mat(data['y0']).T)
            sum1 =  y1 + np.mat(self.input_layer_bias) # Add the bias
            y1 = self.sigmoid(sum1)

            y2 = np.dot(np.array(self.theta2), y1)
            y2 = np.add(y2, self.hidden_layer_bias) # Add the bias
            y2 = self.sigmoid(y2)

            # Step 3: Back propagation
            actual_vals = [0] * 10 # actual_vals is a python list for easy initialization and is later turned into an np matrix (2 lines down).
            actual_vals[data['label']] = 1
            output_errors = np.mat(actual_vals).T - np.mat(y2)
            hidden_errors = np.multiply(np.dot(np.mat(self.theta2).T, output_errors), self.sigmoid_prime(sum1))

            # Step 4: Update weights
            self.theta1 += self.LEARNING_RATE * np.dot(np.mat(hidden_errors), np.mat(data['y0']))
            self.theta2 += self.LEARNING_RATE * np.dot(np.mat(output_errors), np.mat(y1).T)
            self.hidden_layer_bias += self.LEARNING_RATE * output_errors
            self.input_layer_bias += self.LEARNING_RATE * hidden_errors

    def predict(self, test):
        y1 = np.dot(np.mat(self.theta1), np.mat(test).T)
        y1 =  y1 + np.mat(self.input_layer_bias) # Add the bias
        y1 = self.sigmoid(y1)

        y2 = np.dot(np.array(self.theta2), y1)
        y2 = np.add(y2, self.hidden_layer_bias) # Add the bias
        y2 = self.sigmoid(y2)

        results = y2.T.tolist()[0]
        return results.index(max(results))

    def save(self):
        if not self._use_file:
            return

        json_neural_network = {
            "theta1":[np_mat.tolist()[0] for np_mat in self.theta1],
            "theta2":[np_mat.tolist()[0] for np_mat in self.theta2],
            "b1":self.input_layer_bias[0].tolist()[0],
            "b2":self.hidden_layer_bias[0].tolist()[0]
        };
        with open(OCRNeuralNetwork.NN_FILE_PATH,'w') as nnFile:
            json.dump(json_neural_network, nnFile)

    def _load(self):
        if not self._use_file:
            return

        with open(OCRNeuralNetwork.NN_FILE_PATH) as nnFile:
            nn = json.load(nnFile)
        self.theta1 = [np.array(li) for li in nn['theta1']]
        self.theta2 = [np.array(li) for li in nn['theta2']]
        self.input_layer_bias = [np.array(nn['b1'][0])]
        self.hidden_layer_bias = [np.array(nn['b2'][0])]

之后就是神经网络的测试函数了,用神经网络来预知数据

由于我们只有一层隐藏层我们直接计算0-9每个ann的输出抓一个最大的值出来就做完拉

(还有一些没更完会回来挖坑:测试隐藏节点函数错误问题,原因是ocr.py中的test的‘y0'没有按格式写的’u'里(错误原因暂时这么称呼吧)

              save,load细节

              矩阵乘法函数及其内部数据变化过程

              至于算法证明这锅不背了, orz)

原文地址:https://www.cnblogs.com/bitch1319453/p/6691530.html