10分钟搞懂Tensorflow 逻辑回归实现手写识别

1.1.逻辑回归原理1.1.1.逻辑回归

在现实生活中,我们遇到的大部分数据都是非线性的,所以我们不能使用上一章的线性回归方法来拟合数据。但是我们仍然可以从线性模型开始第一步,它从输入数据的加权和开始。

线性模型:

[z=w{x}+b]

其中w称为“权重”,b是偏差,({x})是输入样本数据,都是向量的形式。

让我们先在第二个分类中讨论它。如果我们可以创建一个模型,如果系统输出 1,我们认为它是第一类,如果系统输出 0,我们认为它是第二类。这个输出要求有点像阶跃函数(sea step ),但是阶跃函数是一个不连续的函数,y的值在x=0时突然跳到1。在实际建模中,我们很难在模型中处理这种情况,所以我们使用函数而不是阶跃函数。

功能:

[y=(z)=frac{1}{1+{e}^{-z}}]

该函数是激活函数之一。当 x=0 时,函数值为 0.5。随着x的增大,对应的值接近1,随着x的减小,对应的值接近0。通过这个函数,我们可以得到0到1之间的一系列值,然后我们可以将大于0.的数据除以@>5 归为 1 类,小于 0.5 的数据归为 0 类。

该方法等价于概率估计。我们认为 y 服从伯努利分布,求解给定 x 条件下每个 (y_i) 为 1 或 0 的概率。至此,逻辑回归的抽象术语,这里我们把它变成一个容易理解的概率问题。然后通过最大对数似然函数估计w值,问题就解决了。

(y_i) 等于 1 的概率为:$$(w{x_i}+b)$$

(y_i) 等于 0 的概率为:$$1-(w{x_i}+b)$$

上面对函数的描述说明该函数多用于二分类,我们经常会遇到多分类的问题。这时候,函数的功能就派上用场了。

功能:

该函数也是一种激活函数。主要用于多分类。将输入的线性模型评估为幂指数,最后将输出值归一化为一个概率,对对象进行概率分类,每个对象都不一样。相关的是,所有对象的概率之和为1。对于函数,如果j=2,则和是相同的,也解决了二分类问题。此时,这两个函数都可以用于良好的二元分类。

[(z)_i=frac{e^{z_i}}{sum_{j}{e^{z_j}}}]

上式可以理解为样本为类别(i)的概率。这是:

[y_{_i}=({w}{x}+{b})=frac{e^{w{x_i}+b}}{sum_{j}{e^{w{x_j}+b }}}]

对于回归模型的解释,这里参考别人的图,一张图值一千字。

如果写成多项式,它可以是这样的:

如果改成我们常用的矩阵形式,可以是这样的:

1.1.2. 损失函数

在线性回归中,我们定义了一个由和和方差组成的损失函数,并最小化该函数以找到 (theta) 的最优解。同样,在逻辑回归中,我们也需要定义一个函数,通过最小化这个函数来求解我们的权重 w 值和偏差 b 值。在机器学习中,这个函数可以看作是模型质量的一个指标。这个指标可以称为成本函数(Cost)或损失函数(Loss),然后将这两个函数最小化。这两种方法都一样。

这是一个常见的损​​失函数——“交叉熵”,我们将在下面的示例代码中使用它。交叉熵起源于信息论中的信息压缩编码技术,后来逐渐演变为从博弈论到机器学习等其他领域的重要技术。它被用来衡量我们预测描述真相的效率低下。定义如下:

[H(y_{_i})=-sum_{i}{y_{_{标签}}ln(y_{_i})}]

它是如何派生的?我们先讨论损失函数,然后比较理解。在上面的二分类问题中,我们使用函数,并且我们还假设预测值 (y_i) 服从伯努利分布,那么 (y_i) 等于 1 的概率为:

[frac{1}{1+e^{wx_{_i}+b}}]

(y_i) 等于 0 的概率为:

[1-frac{1}{1+e^{wx_{_i}+b}}]

那么概率密度函数为:

[P(y|x)=(frac{1}{1+e^{wx_{_i}+b}})^{y_{_{标签}}}({1-frac{1}{ 1+e^{wx_{_i}+b}}})^{1-{y_{_{标签}}}}]

上式中的 (y_{_{label}}) 是样本为 1 类的实际概率。然后我们取对数似然函数,然后最小化似然函数进行参数估计(似然函数和此处省略系列词)。

而当我们将问题推广到多分类时,我们也可以得到我们的概率密度函数:

[P(y|x)=(y_i|x)^{y_{_{标签}}}]

我们取概率密度的自然对数的负数,得到我们的似然函数,也就是我们这里所说的交叉熵,其中({y_i})是样本为类别的预测概率 (i) , ({y_{_{label}}}) 是样本是类别 (i) 的实际概率。

[H(y_{_i})=-sum_{i}{{y_{_{label}}}ln(y_{_i})}=-sum_{i}{{y_{_{label}} }ln((wx+b))}]

最后,通过最小化这个交叉熵,找到最优的 w 和 b 值。

1.2. 示例:手写识别系统

现在我们了解了逻辑回归的工作原理,让我们实现一个手写识别系统。首先,我们要挖掘一些数据。我们使用现成的 MNIST 数据集,这是机器学习的入门级数据集。里面包含手写数字的各种图片以及每张图片对应的标签,也就是图片对应的数字(0~9)。可以用一段代码下载,下载前记得安装-mnist

import input_data
mnist = input_data.read_data_sets('MNIST_data/', one_hot = True)

下载的数据共有60000行训练数据集(mnist.train),10000行测试数据集(mnist.test),我们设置图片为x,x是一个shape=[None, 784]一个张量,None代表任意长度,例如可以小于等于mnist.train中的60000张图片。另外,每张图片包含28个像素X 28个像素,向量长度为​​28*28=789,说明该图片是由一个784维向量空间中的点组成的。然后,我们设置图片的标签为,shape=[None,10],这个y_的值就是图片原来对应的标签(0到9的数字)。

使用代码表示可以参考:

x = tf.placeholder("float", [None, 784])  # x定义为占位符,待计算图运行的时候才去读取数据图片
W = tf.Variable(tf.zeros([784, 10]))      # 权重w初始化为0
b = tf.Variable(tf.zeros([10]))           # b也初始化为0
y = tf.nn.softmax(tf.matmul(x, W) + b)    # 创建线性模型
y_ = tf.placeholder("float", [None, 10])  # 图片的实际标签,为0~9的数字

一旦数据准备好,我们就可以开始训练我们的模型了。我们之前讲过这个函数,用这个函数做逻辑回归,我们可以用这段代码来表达:

cross_entropy = -tf.reduce_sum(y_ * tf.log(y))

但是这个函数已经实现了,即:tf.nn.(),没有我们自己定义的(-tf.(y_ * tf.log(y)))。为什么要使用,因为我们在使用这个函数的时候,可能会出现数值不稳定的问题,需要我们自己给函数加一些trick,比较麻烦,模型也比较复杂,所以推荐使用内置的cross-函数将帮助您处理数值不稳定的问题。

-tf.reduce_sum(tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y))

在逻辑回归确定函数之后,我们仍然使用梯度下降来找到最优的 w 和 b 值。最后,整个手写图像识别系统的代码如下:

import numpy as np
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
from mnist import MNIST
mndata = MNIST('MNIST_data')
sess = tf.Session()
x = tf.placeholder("float", [None, 784])
W = tf.Variable(tf.zeros([784, 10]))
b = tf.Variable(tf.zeros([10]))
y = tf.matmul(x, W) + b
y_ = tf.placeholder("float", [None, 10])
# 使用Tensorflow自带的交叉熵函数
cross_entropy = tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y)
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)
init = tf.global_variables_initializer()
sess.run(init)
for i in range(1000):
    batch_xs, batch_ys = mnist.train.next_batch(500)
    sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})
images, labels = mndata.load_testing()
num = 9000
image = images[num]
label = labels[num]
# 打印图片
print(mndata.display(image))
print('这张图片的实际数字是: ' + str(label))
# 测试新图片,并输出预测值
a = np.array(image).reshape(1, 784)
y = tf.nn.softmax(y)  # 为了打印出预测值,我们这里增加一步通过softmax函数处理后来输出一个向量
result = sess.run(y, feed_dict={x: a})  # result是一个向量,通过索引来判断图片数字
print('预测值为:')
print(result)
--result
............................
............................
............................
............................
............................
...............@@@..........
............@@@@@@@.........
...........@@@....@@........
..........@@......@@........
.................@@.........
................@@@.........
..............@@@@..........
............@@@@@@..........
...........@@@@.@@@.........
..................@@........
..................@@........
...................@@.......
...................@@.......
..................@@........
.......@..........@@........
.......@.........@@.........
.......@........@@@.........
.......@@.....@@@...........
........@@@@@@@.............
..........@.................
............................
............................
............................
这张图片的实际数字是: 3
预测值为:
[[ 0.  0.  0.  1.  0.  0.  0.  0.  0.  0.]]

读者可以通过更换不同的图片来尝试预测的结果,可以看出上面的预测还是很不错的。但是我们模型的表现还是需要数​​据说话。测试性能的代码如下:

# 检测预测和真实标签的匹配程度
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1)) 
# 转换布尔值为浮点数,并取平均
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float")) 
# 计算模型在测试数据集上的正确率 
print(sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels})) 
--result
0.9022

这个结果确实不是很好,但是我们可以通过采用其他算法和模型来提高我们的性能,但这超出了本节的范围,我们只需要使用本章来了解逻辑回归是如何工作的。. 未来,我们可以一起讨论改进,以进一步提高模型的准确性。

© 版权声明
THE END
喜欢就支持一下吧
点赞9 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片