本文通过BP神经网络实现一个简单的手写识别系统。
一、基础知识
1环境
python2.7
需要numpy等库
可利用sudo apt-get install python-安装
2神经网络原理
http://www.hankcs.com/ml/back-propagation-neural-network.html
讲的特别清楚,本实验过程中涉及矩阵运算都用numpy库的函数
3.js的基础知识
http://www.w3school.com.cn/tags/html_ref_canvas.asp
canvas的基础知识
http://blog.csdn.net/iamduoluo/article/details/7215639
xmlhttp的介绍
4.BaseHTTPServer库和numpy的使用
二、重要过程
1、如何将手写的图像转化为数据?
将画布分成网格,计算鼠标按下移动的路线经过的方格,例如将画布分为20*20格,则可以构建data[400]的数组,鼠标经过的data[i]=1,可以得到数据输入
drawGrid: function(ctx) {
for (var x = this.PIXEL_WIDTH, y = this.PIXEL_WIDTH; x < this.CANVAS_WIDTH; x += this.PIXEL_WIDTH, y += this.PIXEL_WIDTH) {
ctx.strokeStyle = this.BLUE; //设置颜色
ctx.beginPath();
ctx.moveTo(x, 0); //把焦点移到(x,0)
ctx.lineTo(x, this.CANVAS_WIDTH); //画线(x,0)到(x,canvas_width)
ctx.stroke(); //提交画线
ctx.beginPath();
ctx.moveTo(0, y);
ctx.lineTo(this.CANVAS_WIDTH, y);
ctx.stroke();
}
},
onMouseMove: function(e, ctx, canvas) {
if (!canvas.isDrawing) {
return;
}
this.fillSquare(ctx, e.clientX - canvas.offsetLeft, e.clientY - canvas.offsetTop); //e.clientX是浏览器边缘与鼠标点的x距离,offsetleft是画布边与左侧浏览器边缘的距离
},
onMouseDown: function(e, ctx, canvas) {
canvas.isDrawing = true;
this.fillSquare(ctx, e.clientX - canvas.offsetLeft, e.clientY - canvas.offsetTop);
},
onMouseUp: function(e) {
canvas.isDrawing = false;
},
fillSquare: function(ctx, x, y) {
var xPixel = Math.floor(x / this.PIXEL_WIDTH); //就算坐标
var yPixel = Math.floor(y / this.PIXEL_WIDTH);
//存储手写输入数据
this.data[((xPixel-1) * this.TRANSLATED_WIDTH + yPixel)-1] = 1;
ctx.fillStyle = ‘#ffffff‘;
ctx.fillRect(xPixel * this.PIXEL_WIDTH, yPixel * this.PIXEL_WIDTH, this.PIXEL_WIDTH, this.PIXEL_WIDTH); //正方形填充颜色
},
2、浏览器与客户端的交互
主要通过json 数据交互
sendData: function(json) {
var xmlHttp = new XMLHttpRequest();
xmlHttp.open(‘POST‘, this.HOST + ":" + this.PORT, false);
xmlHttp.onload = function() { this.receiveResponse(xmlHttp); }.bind(this);
xmlHttp.onerror = function() { this.onError(xmlHttp) }.bind(this);
var msg = JSON.stringify(json); //序列化为json
xmlHttp.setRequestHeader(‘Content-length‘, msg.length); //参考给的链接最上
xmlHttp.setRequestHeader("Connection", "close");
xmlHttp.send(msg);
}
服务器
class JSONHandler(BaseHTTPServer.BaseHTTPRequestHandler):
"""处理接收到的POST请求"""
def do_POST(self):
response_code = 200
response = ""
var_len = int(self.headers.get(‘Content-Length‘))
content = self.rfile.read(var_len);
payload = json.loads(content);
# 如果是训练请求,训练然后保存训练完的神经网络
if payload.get(‘train‘):
#print payload[‘trainArray‘]
nn.train(payload[‘trainArray‘],2)
nn.save()
# 如果是预测请求,返回预测值
elif payload.get(‘predict‘):
try:
print nn.predict(data_matrix[0])
response = {"type":"test", "result":str(nn.predict(payload[‘image‘]))}
except:
response_code = 500
else:
response_code = 400
self.send_response(response_code)
self.send_header("Content-type", "application/json")
self.send_header("Access-Control-Allow-Origin", "*")
self.end_headers()
if response:
self.wfile.write(json.dumps(response))
return
3、神经网络的实现
参考500 lines or less
后向传播算法步骤
- 随机初始化参数,对输入利用前向传播计算输出。
- 对每个输出节点按照下式计算delta:
- 对每个隐藏节点按照下式计算delta:
- 计算梯度,并更新权值参数和偏置参数:。这里的是学习率,影响训练速度。
# 前向传播得到结果向量
y1 = np.dot(np.mat(self.theta1), np.mat(data.y0).T)
sum1 = y1 + np.mat(self.input_layer_bias)
y1 = self.sigmoid(sum1)
y2 = np.dot(np.array(self.theta2), y1)
y2 = np.add(y2, self.hidden_layer_bias)
y2 = self.sigmoid(y2)
# 后向传播得到误差向量
actual_vals = [0] * 10
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)) //multiply函数用法,可以简单看为对应相乘
# 更新权重矩阵与偏置向量
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
先写这么多
神经网络中的实现采取了简化的方法,实验效果一般,不过也是一次有趣的实验,下次有待更新神经网络的另一种完全实现。