[机器学习笔记#2] 一步步用 softmax Regression 实现手写字符识别
Cirno2016/07/21软件综合 IP:美国

[机器学习笔记#1]从 Logistic Regression 讲讲分类器和机器学习
[机器学习笔记#3] 简明Lagrange multiplier(拉格朗日乘数)讲义
[资源分享]机器学习资料和常用工具汇总

上一笔记中我们讲了 logistic regression 的基本知识,实现了一个最简单的二分类器。这里再继续将其推广至更多类别的情形。

推广至多类别(multi-class)情形的 logistic regression 分类器被称作 softmax 分类器。这里我们研究的class 从原来的两种变成了C种,相应的logistic function便被如下的softmax function所取代:

$$P(y_i=c|x_i;W) = \frac{\exp(w_{c}^T x_i)}{\sum_{c'} \exp(w_{c'}^T x_i)}=\mu_{ic}$$
其中 $$W = \begin{bmatrix}w_0 ...w_c ... w_C\end{bmatrix} $$
可见权重W变成了C个权重的组合,每一个单独的子权重wc对应一个类别的判定。 类似的,我们定义损失函数如下: $$ L(W) = -log \prod_i^N \prod_c^C P(y_i=c|x_i;W)=\sum_i^N( -(\sum_c^C y_{ic} w_c^T x_i)+log{\sum_{c'}^C{\exp(w_{c'}^T x_i)}})$$
对其求导可得: $$\frac{\partial L(W)}{\partial w_c} =\sum_i^N (\frac{\exp(w_{c}^T x_i)}{\sum_{c'} \exp(w_{c'}^T x_i)}-y_{ic})x_i=\sum_i^N (\mu_{ic}-y_{ic})x_i$$
$$\frac{\partial L(W)}{\partial W} = \begin{bmatrix} \frac{\partial L(W)}{\partial w_0}...\frac{\partial L(W)}{\partial w_c} ... \frac{\partial L(W)}{\partial w_C}\end{bmatrix} $$

这次我们不再使用人工生成的数据作为实验对象,MNIST是一个经典的手写字符识别训练集,由机器学习领域的神人 Yann LeCun 提供,可在其主页下载,同时该页面还提供了大量测试数据以供比较学习。

该数据集包含60,000个训练数据,10,000个测试数据,由大量风格迥异,造型随机的手写数字0~9的黑白图片组成,图片已预先分割裁剪至28x28的尺寸,以下是一组样例:
mnist (1).png

[修改于 8年6个月前 - 2016/08/06 14:51:04]

来自:计算机科学 / 软件综合
5
已屏蔽 原因:{{ notice.reason }}已屏蔽
{{notice.noticeContent}}
~~空空如也
Cirno 作者
8年6个月前 修改于 8年6个月前 IP:美国
823141

在Python中使用MNIST较便捷的方法是安装MNIST工具包,在这里可以下载安装。

安装后便可如下直接导入训练数据和测试数据:

import matplotlib.pyplot as plt
import numpy as np
from mnist import MNIST
%matplotlib inline
mndata = MNIST('Path to your data/MNIST')
images, labels=mndata.load_training()
images_test, labels_test=mndata.load_testing()

计算Loss function,同样得解决指数函数数据溢出的问题:

def calculate_loss(w,x,y):
    theta=np.dot(np.transpose(w),x)
    ### first part
    l1=np.multiply(theta,y)
    ### second part
    thetaMax=np.max(theta,axis=0)
    theta-=thetaMax
    l2=np.log(np.sum(np.exp(theta),axis=0))
    ### sum together
    L=np.sum(-l1+l2+thetaMax)
    return L

计算导数:

def calculate_gradient(w,x_batch,y_batch):
    theta=np.dot(np.transpose(w),x_batch)
    theta-=np.max(theta,axis=0)
    mu=np.exp(theta)
    muSum=np.sum(mu,axis=0)
    mu=mu/muSum
    mu-=y_batch
    dL=np.dot(x_batch,np.transpose(mu))
    return dL

用SGD训练:

def train(x,y,batch_sz,lr,max_iter,loss_thresh):
    ### bias trick ###
    batch_sz=100
    d_len=x.shape[1]
    d_dim=x.shape[0]+1
    d_class=np.max(y)-np.min(y)+1
    x_b=np.concatenate((x,np.ones((1,d_len))),axis=0)
    yc=np.zeros((d_class,d_len))
    yc[y,np.arange(d_len)]=1;
    w=np.zeros((d_dim,d_class),dtype=np.float)
    
    Loss_old=0
    Loss=[]
    stepCnt=0
    ### Run SGD ###
    for iter in range(max_iter):
        ### sample a mini batch ###
        batch=np.arange(d_len)
        np.random.shuffle(batch)
        x_batch=x_b[:,batch[:batch_sz]]
        y_batch=yc[:,batch[:batch_sz]]
        ### update weight ###
        dL=calculate_gradient(w,x_batch,y_batch)
        w-=lr*dL
        ### record loss changes ###
        Loss.append(calculate_loss(w,x_b,yc))
        ### learning rate annealing ###
        stepCnt+=1
        if stepCnt==10:
            stepCnt=0
            lr*=0.8
        ### Check if converge ###
        if abs(Loss[-1]-Loss_old)<loss_thresh: break loss_old="Loss[-1]" return w,loss < code></loss_thresh:>

主函数:

x_train=np.transpose(np.matrix(images))
y_train=np.array(labels)
lr=0.5
batch_sz=50
Max_iter=200
loss_thresh=1e-3
w,Loss = train(x_train,y_train,batch_sz,lr,Max_iter,loss_thresh)
print w
plt.plot(Loss)
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
Cirno作者
8年6个月前 修改于 8年6个月前 IP:美国
823142

Loss function的收敛情况如下:
Loss.png
得到的权重W是一个 785x10 的矩阵,其中10表示有10个不同的类别。

单独拿出其中某一行矩阵,拿掉最后一列bias,是一个784维的向量\(w_c\),即是对应于该行数字的预测权重。

这个权重在机器视觉上有何种意义呢?

下面变一个小魔术,如果你还记得我们的MNIST数据中的单个训练数据\(x_i\),是将一张28x28的数字图片,转换成一个784维的向量得到的。于是我们这里反过来把784维的\(w_c\)转换成28x28的图片,得到了以下的10张图片:
template.png
分别对应0~9这10个数字,从中依稀可以辨别出来。所以,这里分类字符的原理,其实可以看作是模板匹配(template matching),训练过程是为了从训练集中综合出最合适的数字模板。

引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
Cirno作者
8年6个月前 修改于 8年6个月前 IP:美国
823143

简要对测试集做了一下测试,以下补上prediction部分的代码:

def predict(w,x):
    score=np.dot(np.transpose(w),x)
    pred=np.argmax(score)
    return pred

随机抽取40个测试数据做了预测,这里仅定性说明softmax分类器训练结果的有效性,更加具体的测试数据以后更新。令人excited的一点是,虽然这个实验中我们只是用最简单的思路做了一些微小的工作,不加处理的对像素数据进行了regression,准确率已经比较可观了,这很大程度上可以解释为mnist数字,从分层理解的模型中来看,层次是很浅的,大约就是 像素->边缘->基本几何形状 这三层。
result.png result_2.png

引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
novakon
8年6个月前 IP:广东
823147
这个例子所用的算法,很接近传统OCR。但由于MNIST都是手写字体,字形变化很大,所以直接用784维向量而不加处理(比如卷积!)的话,loss func 的收敛情况其实不佳(从模板中8与9的相像程度来看的话)。

感觉楼主准备飙CNN了,期待。
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
Cirno作者
8年6个月前 IP:美国
823185
引用 novakon:
这个例子所用的算法,很接近传统OCR。但由于MNIST都是手写字体,字形变化很大,所以直接用784维向量而不加处理(比如卷积!)的话,loss func 的收敛情况其实不佳(从模板中8与9的相像程度来……
不急不急,NN 还没写呢。 另外回去看了一遍才发现9被我弄掉了,回头补上。
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论

想参与大家的讨论?现在就 登录 或者 注册

所属专业
所属分类
上级专业
同级专业
Cirno
专家 进士 老干部 学者 机友 笔友
文章
34
回复
359
学术分
2
2012/09/03注册,5个月17天前活动

Machine Learning, computer vision enthusiast

Google

主体类型:个人
所属领域:无
认证方式:手机号
IP归属地:未同步
插入公式
评论控制
加载中...
文号:{{pid}}
投诉或举报
加载中...
{{tip}}
请选择违规类型:
{{reason.type}}

空空如也

加载中...
详情
详情
推送到专栏从专栏移除
设为匿名取消匿名
查看作者
回复
只看作者
加入收藏取消收藏
收藏
取消收藏
折叠回复
置顶取消置顶
评学术分
鼓励
设为精选取消精选
管理提醒
编辑
通过审核
评论控制
退修或删除
历史版本
违规记录
投诉或举报
加入黑名单移除黑名单
查看IP
{{format('YYYY/MM/DD HH:mm:ss', toc)}}