最近在学习神经网络的相关知识,特在此做一个笔记。
python语言的功能很强大,可以使用很少的代码实现很多功能,因此大家如果想研究深度学习的话,一定要懂得python语言。
这篇笔记记录我的第一次使用python编写神经网络代码的过程,其中代码基本是借鉴neural networks and deep learning上的知识,这本书对神经网络学习有很大帮助,浅显易懂的深入了CNN的知识,初学者可以研究一下。
好了,多的不说,直接上代码:
这里我做的是最简单的mnist手写字的识别,首先是数据库处理。
这里我下载了42028张mnist图片,每张图片大小是28*28,需要的可以到这里下载。
一般需要把数据转化为pkl文件,便于存储和以后的使用。
import os
import cPickle
import numpy
from PIL import Image
import commondata #自己定义的文件,里面有一些公用的变量
dirname = 'train' #图片根目录
myfile = 'train.pkl' #生成的文件名称
number = 0 #图片个数
for filename in os.listdir(dirname):
filename1 = dirname + '/' + filename
for filename2 in os.listdir(filename1):
number += 1 </span>
pictures = numpy.empty((number,commondata.ImageWidth*commondata.ImageHeight)) #图片文件
lables = numpy.empty(number) #标签文件
index = 0
lable = 0
for filename in os.listdir(dirname):
filename1 = dirname + '/' + filename
for filename2 in os.listdir(filename1):
filename2 = filename1 + '/' + filename2
image = Image.open(filename2)
image = image.convert('L') #为了简便,先统一转化为灰度图片
image = image.resize((commondata.ImageHeight,commondata.ImageWidth))
image_narray = numpy.asarray(image,dtype = 'float64')/256
pictures[index] = numpy.ndarray.flatten(image_narray)
lables[index] = lable
index = index + 1
lable = lable + 1
lables = lables.astype(numpy.int)
write_file=open(myfile,'wb')
cPickle.dump([pictures[:],lables[:]],write_file,-1)
write_file.close()
运行此代码,可以生成一个名字为train.pkl的文件,然后更改根目录和生成文件的名称可以得到test.pkl
生成文件后,还需要调用此文件:
import cPickle
import numpy as np
import commondata
NumClass = commondata.NumClass
ImageWidth = commondata.ImageWidth
ImageHeight = commondata.ImageHeight
def vectorized_result(j):
e = np.zeros((NumClass,1))
e[j] = 1.0
return e
def load_data():
f = open('data.pkl','rb')
training_data = cPickle.load(f)
f.close()
f = open('test.pkl','rb')
test_data = cPickle.load(f)
f.close()
return (training_data,test_data)
def load_data_wrapper():
tr_d,te_d = load_data()
training_inputs = [np.reshape(x,(ImageWidth*ImageHeight,1)) for x in tr_d[0]]
training_results = [vectorized_result(y) for y in tr_d[1]]
training_data = zip(training_inputs,training_results)
test_inputs = [np.reshape(x,(ImageWidth*ImageHeight,1)) for x in te_d[0]]
test_data = zip(test_inputs,te_d[1])
return (training_data,test_data)
这段代码的作用是读取生成的pkl文件,并以标准化的格式返回。
其中vectorized_result的作用是将一个数值转化为向量,比如在手写数字问题中,一共有10个类,需要的向量长度为10,则1应该转化为[0,1,0,0,0,0,0,0,0,0],2应该转化为[0,0,1,0,0,0,0,0,0,0].
接下来便是最核心的代码:
import myalgorithm #自己的算法
import numpy as np
import random
class Network(object):
def __init__(self,sizes): #初始化,sizes的形式一般为[784,50,10,10]之类的
self.num_layers = len(sizes)
self.sizes = sizes
self.biases = [np.random.randn(y,1) for y in sizes[1:]] #偏置初始化
self.weights = [np.random.randn(y,x) #权值初始化
for x,y in zip(sizes[:-1],sizes[1:])]
def feedforward(self,a): #前向传播过程
for b,w in zip(self.biases,self.weights):
a = myalgorithm.sigmoid(np.dot(w,a)+b)
return a
def update_mini_batch(self,mini_batch,eta): #根据一个minibatch训练样本调整参数
nabla_b = [np.zeros(b.shape) for b in self.biases]
nabla_w = [np.zeros(w.shape) for w in self.weights]
for x,y in mini_batch:
delta_nabla_b,delta_nabla_w = self.backprop(x,y)
nabla_b = [nb + dnb for nb,dnb in zip(nabla_b,delta_nabla_b)]
nabla_w = [nw + dnw for nw,dnw in zip(nabla_w,delta_nabla_w)]
self.weights = [w - (eta/len(mini_batch))*nw
for w,nw in zip(self.weights,nabla_w)]
self.biases = [b - (eta/len(mini_batch))*nb
for b,nb in zip(self.biases,nabla_b)]
def backprop(self,x,y): #反向传播
nabla_b = [np.zeros(b.shape) for b in self.biases]
nabla_w = [np.zeros(w.shape) for w in self.weights]
activation = x
activations = [x]
zs = []
for b,w in zip(self.biases,self.weights):
z = np.dot(w,activation)+b
zs.append(z)
activation = myalgorithm.sigmoid(z)
activations.append(activation)
delta = self.cost_derivative(activations[-1],y)*myalgorithm.sigmoid_prime(zs[-1])
nabla_b[-1] = delta
nabla_w[-1] = np.dot(delta,activations[-2].transpose())
for l in xrange(2,self.num_layers):
z = zs[-l]
sp = myalgorithm.sigmoid_prime(z)
delta = np.dot(self.weights[-l+1].transpose(),delta) * sp
nabla_b[-l] = delta
nabla_w[-l] = np.dot(delta,activations[-l-1].transpose())
return (nabla_b,nabla_w)
def evaluate(self,test_data): #测试函数
test_results = [(np.argmax(self.feedforward(x)),y)
for (x,y) in test_data]
return sum(int(x == y)for (x,y) in test_results)
def cost_derivative(self,output_activations,y): #误差函数导函数
return (output_activations - y)
def SGD(self,training_data,epochs,mini_batch_size,eta,test_data = None): #SGD算法
if test_data:
n_test = len(test_data)
n = len(training_data)
for j in xrange(epochs):
random.shuffle(training_data)
mini_batches = [training_data[k:k+mini_batch_size] for k in xrange(0,n,mini_batch_size)]
for mini_batch in mini_batches:
self.update_mini_batch(mini_batch,eta)
if test_data:
print "Epoch {0}:{1}/{2}".format(j,self.evaluate(test_data),n_test)
else:
print "Epoch {0} complete".format(j)
代码解释见注释,
然后便可以训练:
import data
training_data,test_data = data.load_data_wrapper()
import network
net = network.Network([784,30,10])
net.SGD(training_data,30,10,1.5,test_data = test_data)
import cPickle
write_file = open('weight.pkl','wb')
cPickle.dump([net.weights[:],net.biases[:]],write_file,-1)
write_file.close()
训练完成后把权值存储起来。
如果想使用训练的模型进行分类,也很简单,如下代码:
import numpy
import commondata
from PIL import Image
import cPickle
import myalgorithm
filename = 'test1.jpg' #需要分类的文件名
read_file = open('weight.pkl','rb')
weights,biases = cPickle.load(read_file)
read_file.close()
def outcome(weights,biases,a):
for b,w in zip(biases,weights):
a = myalgorithm.sigmoid(numpy.dot(w,a)+b)
return numpy.argmax(a)
def get_predict(filename):
image = Image.open(filename)
image = image.convert('L')
image = image.resize((commondata.ImageHeight,commondata.ImageWidth))
image_narray = numpy.asarray(image,dtype = 'float64')/256
image_vector = numpy.ndarray.flatten(image_narray)
test = numpy.reshape(image_vector,(commondata.ImageHeight*commondata.ImageHeight,1))
result = outcome(weights,biases,test)
print result
调用get_predict函数便可以预测图片所属的类别