上次,我用tnsorflow展示了一两层神经网络,实现8->256的译码功能.
今天,我们继续前行,捣鼓一个可实用化的东东:手写文本识别.
手写识别作为MNIST界的Hello, world,不搞真是说不过去啊.
本示例中共写了两个不同的神经网络,一个是包含两个隐藏层的传统bp网络,一个是包含两层卷积和池化层的cnn网络,用于展示不同网络模型的训练速度和拟合效果.两个网络均能实现图片到字母的转换,bp网络最终准确率在90%,而cnn则轻松到了99%.
手里没有训练图集,本想从网上搞搞,然于我天朝网络受限,从MNIST下东西,可真是难于上青天.于是只好自写了一个文本图片数据生成库,虽不能实用,使用于验证模型的有效性还是足够的.
网络相关的代码(net.py):
#! usr/bin/python #coding=utf-8
import tensorflow as tf
import numpy as np
import random as rnd
#引入自写模块
import fontimg
# 获取数据集(直接调用自写的fontimg模块生成)
# 随机生成26个大写字母的24x24图片,图片除写有字母外,还有随机的噪点以及-60~60度的旋转
# [24,24]的图片点阵数据被转化成归一化后的一维[24x24]灰度数据
def next_batch(bs, tflg = True):
return fontimg.make_datas(bs, tflg)
# 将标签/结果转化成数值
def result2dec(rs):
return np.argmax(rs) #argmax就是返回向量中值最大的维数值
# 验证网络准确率
def do_test(sess, yo_opt, bs):
#获取验证集
bx, by = next_batch(bs, True)
#进行网络运算
yo = sess.run(yo_opt, feed_dict={x: bx, keep_prob : 1.0})
scnt = 0.0
#对比运算结果和标签,计算准确率
for i in range(bs):
if result2dec(yo[i]) == result2dec(by[i]):
scnt = scnt + 1.0
return scnt * 100.0 / bs
#添加一个网络层
def add_layer(x, input_size, output_size, afun = None):
# 随机化隐藏层权重
w = tf.Variable(tf.random_normal([input_size, output_size]))
# 偏置设置为0
b = tf.Variable(tf.zeros([output_size]), "float")
# 设置激活函数
if (afun != None):
y = afun(tf.matmul(x, w) + b)
else :
y = tf.matul(x, w) + b
return y
#普通bp网络,包含2个隐藏层(只要你开心,1个隐藏层不算多,3个隐藏层也不算少,多多少少其实没什么l用)
def make_bp_net(x):
#首个隐藏层,16x16个节点
h0 = add_layer(x, 24 * 24, 16 * 16, tf.nn.sigmoid)
#第二个隐藏层,8x8个节点
h1 = add_layer(h0, 16 * 16, 8 * 8, tf.nn.sigmoid)
#dropout层, 实测没什么l用,所以注掉了
keep_prob = tf.placeholder("float")
#dh1 = tf.nn.dropout(h1, keep_prob)
dh1 = h1
#输出层,26维向量
y = add_layer(dh1, 8 * 8, 26, tf.nn.softmax)
return y, keep_prob
#设置权重
def weight_variable(shape):
initial = tf.truncated_normal(shape, stddev=0.1)
return tf.Variable(initial)
#设置偏置
def bias_variable(shape):
initial = tf.constant(0.1, shape=shape)
return tf.Variable(initial)
#卷积运算,输出图片大小在输入相同
def conv2d(x, w):
return tf.nn.conv2d(x, w, strides=[1, 1, 1, 1], padding='SAME')
#池化运算,输出图片大小高宽各减半
def max_pool_2x2(x):
return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
#卷积网络(照虎画猫而来,不要问我为什么,其实我什么也不知道)
def make_cnn_net(x):
#将一维图片数据转化为2维
x2d = tf.reshape(x, [-1, 24, 24, 1])
#第一个卷积层(5x5过滤器,输出16个特征,只要你开心,160个也可以)
w_c1 = weight_variable([5, 5, 1, 16])
b_c1 = bias_variable([16])
#这里使用relu
h_c1 = tf.nn.relu(conv2d(x2d, w_c1) + b_c1)
#池化
h_p1 = max_pool_2x2(h_c1)
#第二个卷积层(6x6过滤器,只要你开心,4x4,5x5都可以,这次我们输出32个特征)
w_c2 = weight_variable([6, 6, 16, 32])
b_c2 = bias_variable([32])
h_c2 = tf.nn.relu(conv2d(h_p1, w_c2) + b_c2)
h_p2 = max_pool_2x2(h_c2)
#全连接层(两度池化后,图片从24x24 => 12x12 => 6x6, 每个图片32个特征)
#该层我们设计512个节点(只要你开心,513也没问题,真的)
w_fc1 = weight_variable([6 * 6 * 32, 512])
b_fc1 = bias_variable([512])
#将池化后的数据转成1维,好做全连接
h_p2_flat = tf.reshape(h_p2, [-1, 6 * 6 * 32])
#激活函数,也可以使用sigmoid,实测效果差不多
h_fc1 = tf.nn.relu(tf.matmul(h_p2_flat, w_fc1) + b_fc1)
#在输出层之前,我们先做个dropout,传说中能够防止过拟合(实测没有多少l用)
keep_prob = tf.placeholder("float")
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
#h_fc1_drop = h_fc1
#输出层,512到26的全连接
w_fc2 = weight_variable([512, 26])
b_fc2 = bias_variable([26])
y = tf.nn.softmax(tf.matmul(h_fc1_drop, w_fc2) + b_fc2)
return y, keep_prob
#我们开始创建网络,输入为图片数据,输出为图片分类,用传统bp还是cnn,可自选
#设置网络模式
#net = 'bp' #这个网络农村长大的,发育不良,准确率大概大80-90%,训练速度慢
net = 'cnn' #这个网络高大上,训练神速,准确率高,直达99%以上
#先来搞输入层,24x24图片(已转化成灰度一维数组)
x = tf.placeholder("float", [None, 24 * 24])
#再依你所选,我们创建对应网络
if 'bp' == net:
#bp网络
y, keep_prob = make_bp_net(x)
else :
#或者cnn网络
y, keep_prob = make_cnn_net(x)
#最后弄loss层
# 标签占位符
y_ = tf.placeholder("float", [None, 26])
# 最小化交叉熵
loss = -tf.reduce_sum(y_* tf.log(y))
# 不同网络不同训练方法
if 'bp' == net:
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(loss)
else : #也可以使用GradientDescentOptimizer,注意步进要调小
train_step = tf.train.AdamOptimizer(0.001).minimize(loss)
# 在运算之前,先要初始化所有tf变量
init = tf.initialize_all_variables()
# 创建tf会话,并执行init操作
sess = tf.Session(config=tf.ConfigProto(log_device_placement=True))
sess.run(init)
print "%s net training..." % net
# 启动网络训练(运算量稍大,整个过程可能需要数分钟,耐心等待)
eflg = False
for i in range(1000):
#获取训练数据,一次获取100条
bx, by = next_batch(100)
#将数据feed到网络中,执行训练操作
sess.run(train_step, feed_dict={x: bx, y_: by, keep_prob : 0.5})
#每100轮训练,测试一下准确率
if i > 0 and i % 100 == 0:
sr = do_test(sess, y, 100)
print i, ' \t: ', sr
if (sr > 99.0): eflg = True
#准确度达标,提前退出训练
if eflg : break
# 我们再测试一下准确率
print '###last rate \t:', do_test(sess, y, 1000)
# 最后,我们用网络来识别一下字符串,别捣鼓了半天,什么用都木体现
chs = [chr(i) for i in range(65, 91)] #大写字母表
# 按指定字串生成数据(注意这能只能是大写字母)
text = 'TENSORFLOW'
ims, lables = fontimg.make_string_datas(text)
# 让网络执行y运算,将图片转化为文字
letters = sess.run(y, feed_dict={x: ims, keep_prob : 1.0})
# 显示识别结果(编号 : 识别值 - 真实值 - 标签值)
for i in range(len(text)):
print "[%.2d] : " % (i + 1), chs[result2dec(letters[i])], '-', text[i], '-', chs[result2dec(lables[i])]
#关闭会话,回收资源,要养成好习惯噢!
sess.close()图片生存器代码(fontimg.py):
#! usr/bin/python #coding=utf-8
import PIL.Image as Image
import PIL.ImageDraw as ImageDraw
import PIL.ImageFont as ImageFont
import PIL.ImageFilter as ImageFilter
import random as rnd
# 图片数据生成程序
class FontImg(object):
def __init__(self, img_size, fnt_size, fnt_file = ''):
self.img_size = img_size
self.fnt_size = fnt_size
if '' == fnt_file : fnt_file = './font.otf'
self.font = ImageFont.truetype(fnt_file, fnt_size)
#创建一个字符图片
def create_img(self, ch, bg_color, fnt_color):
img = Image.new("RGB",[self.img_size, self.img_size], bg_color)
dopt = ImageDraw.Draw(img)
dopt.rectangle([0, 0, self.img_size - 1, self.img_size - 1], fill = bg_color)
loc = dopt.textsize(ch, font = self.font)
xp = (self.img_size - loc[0]) / 2
yp = (self.img_size - loc[1]) / 2 - 2
dopt.text([xp, yp], ch, fill = fnt_color, font = self.font)
return img
#为图片添加随机噪点
def noise_img(self, img, dots, dot_color, dopt):
for i in range(dots):
x = rnd.randint(0, self.img_size - 1)
y = rnd.randint(0, self.img_size - 1)
dopt.point([(x, y)], dot_color)
#创建一个图片,并对其进行随机旋转及添加噪点
def make_img(self, ch, dflg = False):
img = self.create_img(ch, 'black', 'white')
if dflg:
#随机旋转-60~60度
if (rnd.randint(0, 100) > 30):
img = img.rotate(rnd.randint(-60, 60))
#随机噪点
dopt = ImageDraw.Draw(img)
self.noise_img(img, rnd.randint(0, self.img_size * self.img_size / 6), 'white', dopt)
return img
#将图片数据转化成归一化后的灰度一维数据
def make_img_data(self, data, ch, sflg):
img = self.make_img(ch, sflg)
ps = img.convert('L').load()
for y in range(self.img_size):
for x in range(self.img_size):
data[y * self.img_size + x] = float(ps[x, y]) / 255.0
#随机生成图片数据,bs指定图片个数
def make_data(self, bs, sflg = False, string = ''):
chs = [chr(i) for i in range(65, 91)]
isize = self.img_size * self.img_size
if (string != ''): #指定字符串的转换
bs = len(string)
rflg = False
else :
rflg = True
x = [[0.0 for i in range(isize)] for i in range(bs)]
y = [[0.0 for i in range(len(chs))] for i in range(bs)]
for b in range(bs):
if rflg :
cindx = rnd.randint(0, len(chs) - 1)
ch = chs[cindx]
else :
ch = string[b]
cindx = ord(ch) - ord('A')
if (cindx < 0 or cindx > 25) :
cindx = 0
ch = 'A'
#print 'error!'
y[b][cindx] = 1.0
self.make_img_data(x[b], ch, sflg)
return x, y
#随机生成图片数据
def make_datas(bs, tflg = False):
fm = FontImg(24, 20)
return fm.make_data(bs, tflg)
#指定生成图片数据
def make_string_datas(chs):
fm = FontImg(24, 20)
return fm.make_data(0, False, chs)
if __name__ == '__main__':
fm = FontImg(24, 20)
im = fm.make_img('B', True)
im.save('a.bmp')
#x, y = make_datas(1)
x, y = make_string_datas('ABC')
print x
print y弃caffe和c++,顺利转向tensorflow和python的怀抱,先把神经网络的Hello, word搞起!
#! usr/bin/python #coding=utf-8
import tensorflow as tf
import numpy as np
import random as rnd
# 本示例使用一个两层神经网络,训练后可实现8位二进制数到10进制的转换
# 输入: 8维向量,用于表示二进数对应的8个bit位
# 输出: 256维向量,二进数的十进制值为n,向量的第n维值为1.0,其于所有维的值为0.0
# 显然,这是一个标准的softmax回归模型
# 实际上,使用单层网络实际目标不是难事,本示例使用两层网络,仅仅只是为了演示.
# @@首先,我们先写几个函数,用于自动生成训练和测试数据
# 数据产生函数,用于生成训练数据(参数bs指定生成数据条数)
def next_batch(bs):
#输入为一个8维向量,每个维度表示二进制数对应位的值
xs = [[0.0 for i in range(8)] for j in range(bs)]
#标签为一个256维向量,若二进制输入对应的十进制数为n时,其对应维的值为1.0,其余维值均为0
ys = [[0.0 for i in range(256)] for j in range(bs)]
#随机生成数据
for j in range(bs):
b = 0
for i in range(8): #随机生成二进制数据
xs[j][i] = rnd.choice([0.0, 1.0])
if (xs[j][i] > 0.5):
b = b * 2 + 1
#设置对应的标签值
ys[j][b % 256] = 1.0
#同时返回数据及对应标签
return xs, ys
# 将标签/结果转化成数值
def result2dec(rs):
return np.argmax(rs) #argmax就是返回向量中值最大的维数值
# 验证网络准确率
def do_test(sess, yo_opt, bs):
#获取验证集
bx, by = next_batch(bs)
#进行网络运算
yo = sess.run(yo_opt, feed_dict={x: bx})
scnt = 0.0
#对比运算结果和标签,计算准确率
for i in range(bs):
if result2dec(yo[i]) == result2dec(by[i]):
scnt = scnt + 1.0
return scnt * 100.0 / bs
# @@其次,我们开始构架网络
#step1: 构造输入层,这里使用占位符,用于训练过程动态输入数据
x = tf.placeholder("float", [None, 8])
#step2: 构造隐藏层,8个结点,与输入层全连接
# 随机化隐藏层权重
wh = tf.Variable(tf.random_normal([8, 8]))
# 偏置设置为0
bh = tf.Variable(tf.zeros([8]), "float")
# 隐藏层输出,激活函数选择了sigmoid
oh = tf.nn.sigmoid(tf.matmul(x, wh) + bh)
#step3: 构造输出层,256个节点,与隐藏层全连接
# 随机化输出层权重
wo = tf.Variable(tf.random_normal([8, 256]))
# 偏置设置为0
bo = tf.Variable(tf.zeros([256]), "float")
# 输出,激活函数当然是softmax
y = tf.nn.softmax(tf.matmul(oh, wo) + bo)
#step4: 构架loss层
# 标签占位符,用于动态输入标签数据到网络
y_ = tf.placeholder("float", [None, 256])
# 计算交叉熵(别问为什么,我只是搬运工,y_, y不要对调噢)
loss = -tf.reduce_sum(y_* tf.log(y))
# 也可以用方差做为损失函数玩玩,效果(收敛速度)可能要差点
#loss = tf.reduce_sum(tf.square(y - y_))
# 训练方向为使交叉熵(loss)最小
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(loss)
# 至此,神经网络已经搭建完毕,是时候启动运算了
# 在运算之前,先要初始化所有tf变量
init = tf.initialize_all_variables()
# 创建tf会话,并执行init操作
sess = tf.Session(config=tf.ConfigProto(log_device_placement=True))
sess.run(init)
# @@来吧,我们可对网络进行训练了
eflg = False
for i in range(50000):
#获取训练数据,一次获取100条
bx, by = next_batch(100)
#将数据feed到网络中,运算吧,tf君!
sess.run(train_step, feed_dict={x: bx, y_: by})
#每100轮训练,测试一下准确率
if i > 0 and i % 100 == 0:
sr = do_test(sess, y, 100)
print i, ' \t: ', sr
if (sr > 99.0): eflg = True
#准确度达标,退出训练
if eflg : break
# @@最后,我们再测试一下准确率
print '###last rate \t:', do_test(sess, y, 10000)
# 关闭会话,回收资源
sess.close()
# @@好吧,程序写完了,运行之,你可能得到这样的结果:
100 : 51.0
200 : 55.0
300 : 84.0
400 : 83.0
500 : 80.0
600 : 95.0
700 : 98.0
800 : 91.0
900 : 93.0
1000 : 90.0
1100 : 95.0
1200 : 96.0
1300 : 96.0
1400 : 96.0
1500 : 95.0
1600 : 97.0
1700 : 95.0
1800 : 98.0
1900 : 97.0
2000 : 99.0
2100 : 100.0
###last rate : 99.29
# @@恩恩,随着训练次数的增加,准确率稳步提升,最后训练出的模型大概有99%的准确率. watchpoint,顾名思义,其一般用来观察某个变量/内存地址的状态(也可以是表达式),如可以监控该变量/内存值是否被程序读/写情况。在我们跟踪一些变量被意外改写的问题(如越界)时,可能有奇效....
google被墙,一些专业资料度娘查起来又经常无力,肿么办,肿么办?两步搞定:
step 1 修改/etc/hosts,加入如下内容:
#gle.com.hk
220.255.2.153 www.google.com.hk
220.255.2.153 accounts.google.com.hk
220.255.2.153 clients1.google.com.hk
220.255.2.153 desktop.google.com.hk
220.255.2.153 encrypted.google.com.hk
220.255.2.153 gxc.google.com.hk
220.255.2.153 video.google.com.hk
220.255.2.153 id.google.com.hk
220.255.2.153 mobile.google.com.hk
220.255.2.153 picasaweb.google.com.hk
step 2 打开浏览器,访问https://www.google.com.hk/
是不是很神奇,又可以愉快地查资料了。
以上方法在ubuntu+firefox上已亲测可用,注意url开头一定是https噢。
windows下方法类似,修改hosts内容就可。
上周对十一月份的代码评审情况进行了一次审查,发现一个有趣的问题:某两个部门有不少人不会用或者是不喜欢用我们的代码评审系统,原因也很简单:不好用!结果也很清晰,这两个部门的代码评审数据非常惨淡,在六个部门中垫底.既然有其他部门做得更好,而且数据差距比较大,首先想到的是态度问题,当场对落后部门的负责人进行了批评并做了绩效处罚的决定....
函数中随处return,是造成我们资源泄露和程序死锁的主要根源。很多同志写过类似的代码,函数中创建了和引用了多个资源,中间使用的过程中出错了,程序return,经典的代码是这样的:
void fun()
{
Lock(mutex)
mem = malloc(size);
if (null == mem)
{
return; //死锁
}
fh = fopen(“test.txt”);
if (fh)
{
return; //死锁+内存泄露
}
if (fwrite(“abc”, fh) < 0)
{
return; //死锁+内存泄露+句柄泄露
}
fclose(fh);
free(mem);
Unlock(mtex)
}...
这几天原本可得的一点清静,被几个无聊的软件问题无端打扰了.我们几个计划这个月底要了掉的项目,出了一些岔子,设备测试或者老化运行期间,出了无故重启或者功能异常.项目组的人自己捣鼓了几天,没什么有进展,来我这求方案,而我能够得到的信息,却只是最后设备不如预期地工作了,其他与之相关的线索,要嘛嘛没有.
这样的情景,在我并不漫长的工作生涯中,已不知多少次重演.感谢我们那些懒惰而又恐慌程序员,这些年把我训练成了处理这些无头公案的专家....
最近有个嵌入式研发项目又被Linux out of memory(OOM)给盯上了,安排人力突了两周多时间,进展不是很大,为之不多的收获如下:
1 oom并非内存泄漏和系统内存配置不足导致,真凶是内存碎片:512M的os内存,经常在还有200-300M的free余量下产生oom,buddy系统中有成千上万的4K,8K碎片.
2 oom经常发生在vfs do_write或者net模块中的new skb接口中,实际上当时的内存碎片中也有匹配的内存块存在,但还是oom了.
3 我们的同志分析,可能与linux cma内存管理有关....
欢迎光临悟会的博客
**您是本博第50147位访客**
**今日访问量:89次**
**总点击数:68938次**
第三方登录