项目介绍:
本课程的目标是通过分析用户上传的血常规图片来预测出用户的性别和年龄。
项目演示:
版本库URL:https://coding.net/u/wjiafengw/p/np2016-1765748544/git
1:上传图片并点击提交
注意:图片必须为jpg/jgeg/png格式并且化验单应尽量平整
注意:报告表格里的数字为ocr对化验单的识别,识别结果可能有错,需要手动校正
3:点击“predict”
运行和环境搭建:
安装numpy
$ sudo apt-get install Python-numpy
安装OpenCV
$ sudo apt-get install python-opencv
安装pytesseract
$ sudo apt-get install tesseract-ocr
$ sudo pip install pytesseract
$ sudo apt-get install python-tk
$ sudo pip install pillow
安装Flask框架
$ sudo pip install Flask
安装mongodb
$ sudo apt-get install mongodb # 如果提示no modulename mongodb, 先执行sudo apt-get update
$ sudo service mongodb started
$ sudo pip install pymongo
安装Tensorflow
$ sudo apt-get install python-numpy
$ sudo apt-get install python-imaging
$ pip install --upgradehttps://storage.googleapis.com/tensorflow/Linux/cpu/tensorflow-0.12.0rc0-cp27-none-linux_x86_64.whl
运行
$ cd BloodTestReportOCR
$ python view.py # upload图像,在浏览器打开http://yourip:8080
个人对项目的理解:
我认为这个项目有三个难点:1)项目整体框架的搭建 2)图片的处理和ocr识别 3)利用机器学习算法进行预测
Pull && Request:
第一个pull是用RNN模型对性别进行预测,正确率0.7左右(merge)
第二个pull是将RNN集成到A2中,性别准确率0.7,年龄0.3(将年龄划分成10个区间)
并且修改了config.py配置文件,使得可以自由选择模型(未处理)
图像处理部分:
由于之前没有接触过图像处理,自己也对这部分没有代码贡献。通过学习,还是能大概看懂这部分的流程:
从前端接收图片(view.upload),为了进行ocr识别(ImageFilter.ocr),他会先经过一个过滤器(ImageFilter.filter)过滤掉不合格的图片,然后进行透视变换(ImageFilter.perspect),这也是图片处理的核心。其原理是根据某个阈值用长方形ROI去提取表格中的三条黑线,然后根据三条线对倾斜的报告单照片执行了变换,并将表格区域转化成一张1000*760的图保存。之后,对该图片进行剪切(ImageFilter.autocut),保存在temp_pics。为了提高识别的准确率,会有一些灰度化、二值化、膨胀等方法(Imgproc.digiting)对裁剪过的小图进行处理,然后调用外部API进行识别(pytesseract.image_to_string),并将其结果封装成json返回。最后,将图片信息和json保存到数据库。
机器学习部分:
目前深度学习框架有很多,tensorflow、caffe、paddlepaddle等等,各有优势和特色,本人选择的是tensorflow。线上的tensorflow代码采用的是双隐层的多层感知机的模型,由于之前了解过RNN相关的知识,自然想到了在tensorflow下用RNN作为模型来预测性别和年龄并且将它集成到A2。RNN大家都有所了解,特别适合用来做自然语言的处理,但并不意味着它不能用来预测其他东西。在这个项目中,使用RNN这个模型效果尚可。我们甚至不用进行去均值、归一化等方法就能达到性别70%以上的准确率。然而预测年龄的准确率还是比较低,我采用的是将年龄划分成10个区间,最后的准确率也只能接近30%。由于我在训练、预测的时候性别和年龄是封装在不同函数里,而这两部分代码高度相似,所以以下只贴年龄部分的代码:
训练RNN模型并保存模型:
# Parameters
learning_rate = 0.001
training_iters = 200
display_step = 10
# Network Parameters
n_input = 11 #每个时间步骤传入11个特征
n_steps = 2 #分为2个时间步骤
n_hidden = 128 #隐藏层神经元的个数
n_classes = 10 # 将年龄分成10类
#定义年龄的one_hot
def one_hot(a, length):
b = np.zeros([length, 10])
for i in range(length):
tmp = a[i] / 10
b[i][int(tmp)] = 1
return b
#读数据集,共2058行,24列(第一列为性别,第二列为年龄)
data = np.loadtxt(open("./data.csv","rb"),delimiter=",",skiprows=0)
#我们取前1858行作为我们的训练集
train_data = data[0:1858, :]
#剩余的作为测试集
test_data = data[1858:, :]
#第二列是年龄
train_label_age = train_data[:, 1:2]
#转one_hot
train_label_age = one_hot(train_label_age,train_data.shape[0])
train_data = train_data[:, 2:]
#转成要求的格式
train_data = np.reshape(train_data, (1858,n_steps,n_input))
#下面对测试集的处理同上
test_label_age = test_data[:, 1:2]
test_label_age = one_hot(test_label_age,test_data.shape[0])
test_data = test_data[:, 2:]
test_data = np.reshape(test_data, (200,n_steps,n_input))
# tf Graph input
x = tf.placeholder("float", [None, n_steps, n_input])
y = tf.placeholder("float", [None, n_classes])
# Define weights
weights = {
'out': tf.Variable(tf.random_normal([n_hidden, n_classes]))
}
biases = {
'out': tf.Variable(tf.random_normal([n_classes]))
}
def RNN(x, weights, biases):
#下面这三行的代码主要是把我们传进来的数据(batch_size,n_steps,n_input)这种形状的数据
#分为n_steps个时间步骤,每个时间步骤数据的形状是(batch_size,n_input)
#矩阵的转置
#将(batch_size, n_steps, n_input)变为(n_steps, batch_size, n_input)
#此例中shape从(1858,2,11)变为(2,1858,11)
x = tf.transpose(x, [1, 0, 2])
#降维
#将(n_steps, batch_size, n_input)变为 (n_steps*batch_size, n_input)
#此例中shape从(2,1858,11)变为(2*1858,11)
x = tf.reshape(x, [-1, n_input])
#拆分
#此例拆分为:split0:shape:(1858,11)和split1:shape:(1858,11)
#经过这一系列操作将数据拆分成两个时间步骤,每个时间步骤传入的是一个1858行11列的矩阵
x = tf.split(0, n_steps, x)
# Define a lstm cell with tensorflow
lstm_cell = rnn_cell.BasicLSTMCell(n_hidden, forget_bias=1.0)
# Get lstm cell output
outputs, states = rnn.rnn(lstm_cell, x, dtype=tf.float32)
# output[-1]为上个时间的记忆,形状为(1858,128)
# weights[‘out’]形状为(128,10)
# return的形状为(1858,10)
return tf.matmul(outputs[-1], weights['out']) + biases['out']
pred = RNN(x, weights, biases)
# Define loss and optimizer
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(pred, y))
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost)
# Evaluate model
correct_pred = tf.equal(tf.argmax(pred,1), tf.argmax(y,1))
accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))
# Initializing the variables
init = tf.global_variables_initializer()
#为了保存模型
saver=tf.train.Saver()
# Launch the graph
with tf.Session() as sess:
sess.run(init)
step = 1
# Keep training until reach max iterations
while step < training_iters:
sess.run(optimizer, feed_dict={x: train_data, y: train_label_age})
if step % display_step == 0:
# Calculate batch accuracy
acc = sess.run(accuracy, feed_dict={x: train_data, y: train_label_age})
# Calculate batch loss
loss = sess.run(cost, feed_dict={x: train_data, y: train_label_age})
#每迭代10次打印出当前的准确率和损失
print("Iter " + str(step) + ", Loss= " + \
"{:.6f}".format(loss) + ", Training Accuracy= " + \
"{:.5f}".format(acc))
step += 1
print("Optimization Finished!")
#保存我们的训练模型
save_path = saver.save(sess, "./rnn_model/rnn_age_model/model.ckpt")
#print("model save in file:%s" %save_path)
#打印出测试集的准确率
print("Testing Accuracy:", \
sess.run(accuracy, feed_dict={x: test_data, y: test_label_age}))
下面是运行的结果:
def predict():
print ("predict now!")
data = json.loads(request.form.get('data'))
ss = data['value']
arr = numpy.array(ss)
arr = numpy.reshape(arr, [1, 22])
if app.config['MODEL'] == 0:
sex = rnn_predict.predict_sex(arr)
age = rnn_predict.predict_age(arr)
result = {
"sex": sex,
"age": age
}
elif app.config['MODEL'] == 1:
sex, age = tf_predict.predict(arr)
result = {
"sex": sex,
"age": int(age)
}
return json.dumps(result)
rnn_predict.py
def predict_age(data_predict):
tf.reset_default_graph()
# Network Parameters
n_input = 11
n_steps = 2
n_hidden = 128
n_classes = 10
data_predict = np.reshape(data_predict, (1,n_steps, n_input))
# tf Graph input
x = tf.placeholder("float", [None, n_steps, n_input])
y = tf.placeholder("float", [None, n_classes])
# Define weights
weights = {
'out': tf.Variable(tf.random_normal([n_hidden, n_classes]))
}
biases = {
'out': tf.Variable(tf.random_normal([n_classes]))
}
def RNN(x, weights, biases):
x = tf.transpose(x, [1, 0, 2])
x = tf.reshape(x, [-1, n_input])
x = tf.split(0, n_steps, x)
# Define a lstm cell with tensorflow
lstm_cell = rnn_cell.BasicLSTMCell(n_hidden, forget_bias=1.0)
# Get lstm cell output
outputs, states = rnn.rnn(lstm_cell, x, dtype=tf.float32)
# Linear activation, using rnn inner loop last output
return tf.matmul(outputs[-1], weights['out']) + biases['out']
pred = RNN(x, weights, biases)
# Initializing the variables
init = tf.global_variables_initializer()
saver = tf.train.Saver()
# Launch the graph
with tf.Session() as sess:
sess.run(init)
#读之前保存的模型
saver.restore(sess,"./rnn_model/rnn_age_model/model.ckpt")
#预测
p = sess.run(pred, feed_dict={x:data_predict})
# print(tf.argmax(p, 1))
# 找出最大的得分向量
max = p[0][0]
max_i = 0
for i in range(n_classes):
if p[0][i] > max:
max_i = i
max = p[0][i]
age_result = str(max_i * 10) + "~" + str((max_i+1) *10 -1)
return age_result