机器学习和计算机视觉相关的数学

以为MIT的牛人首创,我转载于这里

    感觉数学似乎总是不够的。这些日子为了解决research中的一些问题,又在图书馆捧起了数学的教科书。从大学到现在,课堂上学的和自学的数学其实不算少了,可是在研究的过程中总是发现需要补充新的数学知识。Learning和Vision都是很多种数学的交汇场。看着不同的理论体系的交汇,对于一个researcher来说,往往是非常exciting的enjoyable的事情。不过,这也代表着要充分了解这个领域并且取得有意义的进展是很艰苦的。 记得在两年前的一次blog里面,提到过和learning有关的数学。今天看来,我对于数学在这个领域的作用有了新的思考。

1、 Linear Algebra (线性代数)Statistics (统计学) 是最重要和不可缺少的。这代表了Machine Learning中最主流的两大类方法的基础。一种是以研究函数和变换为重点的代数方法,比如Dimension reduction,feature extraction,Kernel等,一种是以研究统计模型和样本分布为重点的统计方法,比如Graphical model, Information theoretical models等。它们侧重虽有不同,但是常常是共同使用的,对于代数方法,往往需要统计上的解释,对于统计模型,其具体计算则需要代数的帮助。 以代数和统计为出发点,继续往深处走,我们会发现需要更多的数学。

2、Calculus (微积分),只是数学分析体系的基础。其基础性作用不言而喻。Learning研究的大部分问题是在连续的度量空间进行的,无论代数还是统计,在研究优化问题的时候,对一个映射的微分或者梯度的分析总是不可避免。而在统计学中,Marginalization和积分更是密不可分——不过,以解析形式把积分导出来的情况则不多见。

3、Partial Differential Equation (偏微分方程),这主要用于描述动态过程,或者仿动态过程。这个学科在Vision中用得比Learning多,主要用于描述连续场的运动或者扩散过程。比如Level set, Optical flow都是这方面的典型例子。

4、Functional Analysis (泛函分析), 通俗地,可以理解为微积分从有限维空间到无限维空间的拓展——当然了,它实际上远不止于此。在这个地方,函数以及其所作用的对象之间存在的对偶关系扮演了非常重要的角色。Learning发展至今,也在向无限维延伸——从研究有限维向量的问题到以无限维的函数为研究对象。Kernel Learning 和 Gaussian Process 是其中典型的例子——其中的核心概念都是Kernel。很多做Learning的人把Kernel简单理解为Kernel trick的运用,这就把kernel的意义严重弱化了。在泛函里面,Kernel (Inner Product) 是建立整个博大的代数体系的根本,从metric, transform到spectrum都根源于此。

5、Measure Theory (测度理论),这是和实分析关系非常密切的学科。但是测度理论并不限于此。从某种意义上说,Real Analysis可以从Lebesgue Measure(勒贝格测度)推演,不过其实还有很多别的测度体系——概率本身就是一种测度。测度理论对于Learning的意义是根本的,现代统计学整个就是建立在测度理论的基础之上——虽然初级的概率论教科书一般不这样引入。在看一些统计方面的文章的时候,你可能会发现,它们会把统计的公式改用测度来表达,这样做有两个好处:所有的推导和结论不用分别给连续分布和离散分布各自写一遍了,这两种东西都可以用同一的测度形式表达:连续分布的积分基于Lebesgue测度,离散分布的求和基于计数测度,而且还能推广到那种既不连续又不离散的分布中去(这种东西不是数学家的游戏,而是已经在实用的东西,在Dirchlet Process或者Pitman-Yor Process里面会经常看到)。而且,即使是连续积分,如果不是在欧氏空间进行,而是在更一般的拓扑空间(比如微分流形或者变换群),那么传统的黎曼积分(就是大学一年级在微积分课学的那种)就不work了,你可能需要它们的一些推广,比如Haar Measure或者Lebesgue-Stieltjes积分。

6、Topology(拓扑学),这是学术中很基础的学科。它一般不直接提供方法,但是它的很多概念和定理是其它数学分支的基石。看很多别的数学的时候,你会经常接触这样一些概念:Open set / Closed set,set basis,Hausdauf, continuous function,metric space, Cauchy sequence, neighborhood, compactness, connectivity。很多这些也许在大学一年级就学习过一些,当时是基于极限的概念获得的。如果,看过拓扑学之后,对这些概念的认识会有根本性的拓展。比如,连续函数,当时是由epison法定义的,就是无论取多小的正数epsilon,都存在xxx,使得xxx。这是需要一种metric去度量距离的,在general topology里面,对于连续函数的定义连坐标和距离都不需要——如果一个映射使得开集的原像是开集,它就是连续的——至于开集是基于集合论定义的,不是通常的开区间的意思。这只是最简单的例子。当然,我们研究learning也许不需要深究这些数学概念背后的公理体系,但是,打破原来定义的概念的局限在很多问题上是必须的——尤其是当你研究的东西它不是在欧氏空间里面的时候——正交矩阵,变换群,流形,概率分布的空间,都属于此。

7、Differential Manifold (微分流形), 通俗地说它研究的是平滑的曲面。一个直接的印象是它是不是可以用来fitting一个surface什么的——当然这算是一种应用,但是这是非常初步的。本质上说,微分流形研究的是平滑的拓扑结构。一个空间构成微分流形的基本要素是局部平滑:从拓扑学来理解,就是它的任意局部都同胚于欧氏空间,从解析的角度来看,就是相容的局部坐标系统。当然,在全局上,它不要求和欧氏空间同胚。它除了可以用于刻画集合上的平滑曲面外,更重要的意义在于,它可以用于研究很多重要的集合。一个n-维线性空间的全部k-维子空间(k ……貌似不完整

8、Lie Group Theory (李群论),一般意义的群论在Learning中被运用的不是很多,群论在Learning中用得较多的是它的一个重要方向Lie group。定义在平滑流行上的群,并且其群运算是平滑的话,那么这就叫李群。因为Learning和编码不同,更多关注的是连续空间,因为Lie group在各种群中对于Learning特别重要。各种子空间,线性变换,非奇异矩阵都基于通常意义的矩阵乘法构成李群。在李群中的映射,变换,度量,划分等等都对于Learning中代数方法的研究有重要指导意义。

 
9、Graph Theory(图论),图,由于它在表述各种关系的强大能力以及优雅的理论,高效的算法,越来越受到Learning领域的欢迎。经典图论,在Learning中的一个最重要应用就是graphical models了,它被成功运用于分析统计网络的结构和规划统计推断的流程。Graphical model所取得的成功,图论可谓功不可没。在Vision里面,maxflow (graphcut)算法在图像分割,Stereo还有各种能量优化中也广受应用。另外一个重要的图论分支就是Algebraic graph theory (代数图论),主要运用于图的谱分析,著名的应用包括Normalized Cut和Spectral Clustering。近年来在semi-supervised learning中受到特别关注。

Posted in Computer Vision, Data Mining, General Development | Leave a comment

Delaunay triangulation

delaunay -
Delaunay triangulation
Syntax

TRI = delaunay(x,y)
TRI = delaunay(x,y,options)
Definition

image 

Given a set of data points, the Delaunay triangulation is a set of lines connecting each point to its natural neighbors. The Delaunay triangulation is related to the Voronoi diagram — the circle circumscribed about a Delaunay triangle has its center at the vertex of a Voronoi polygon.

Description

TRI = delaunay(x,y) for the data points defined by vectors x and y, returns a set of triangles such that no data points are contained in any triangle's circumscribed circle. Each row of the m-by-3 matrix TRI defines one such triangle and contains indices into x and y. If the original data points are collinear or x is empty, the triangles cannot be computed and delaunay returns an empty matrix.

delaunay uses Qhull.

TRI = delaunay(x,y,options) specifies a cell array of strings options to be used in Qhull via delaunayn. The default options are {'Qt','Qbb','Qc'}.

If options is [], the default options are used. If options is {''}, no options are used, not even the default. For more information on Qhull and its options, see http://www.qhull.org.
Remarks

The Delaunay triangulation is used by: griddata (to interpolate scattered data), voronoi (to compute the voronoi diagram), and is useful by itself to create a triangular grid for scattered data points.

The functions dsearch and tsearch search the triangulation to find nearest neighbor points or enclosing triangles, respectively.
Visualization

Use one of these functions to plot the output of delaunay:
triplot   
Displays the triangles defined in the m-by-3 matrix TRI. See Example 1.

trisurf   
Displays each triangle defined in the m-by-3 matrix TRI as a surface in 3-D space. To see a 2-D surface, you can supply a vector of some constant value for the third dimension. For example
trisurf(TRI,x,y,zeros(size(x)))

image

See Example 2.

trimesh   
Displays each triangle defined in the m-by-3 matrix TRI as a mesh in 3-D space. To see a 2-D surface, you can supply a vector of some constant value for the third dimension. For example,
trimesh(TRI,x,y,zeros(size(x)))

image

produces almost the same result as triplot, except in 3-D space. See Example 2.

Examples
Example 1

Plot the Delaunay triangulation for 10 randomly generated points.
rand('state',0);
x = rand(1,10);
y = rand(1,10);
TRI = delaunay(x,y);
subplot(1,2,1),...
triplot(TRI,x,y)
axis([0 1 0 1]);
hold on;
plot(x,y,'or');
hold off

image

Compare the Voronoi diagram of the same points:
[vx, vy] = voronoi(x,y,TRI);
subplot(1,2,2),...
plot(x,y,'r+',vx,vy,'b-'),...
axis([0 1 0 1])

Example 2

Create a 2-D grid then use trisurf to plot its Delaunay triangulation in 3-D space by using 0s for the third dimension.
[x,y] = meshgrid(1:15,1:15);
tri = delaunay(x,y);
trisurf(tri,x,y,zeros(size(x)))

Next, generate peaks data as a 15-by-15 matrix, and use that data with the Delaunay triangulation to produce a surface in 3-D space.
z = peaks(15);
trisurf(tri,x,y,z)

You can use the same data with trimesh to produce a mesh in 3-D space.
trimesh(tri,x,y,z)

image 

Example 3

The following example illustrates the options input for delaunay.
x = [-0.5 -0.5 0.5 0.5];
       y = [-0.5 0.5 0.5 -0.5];

The command
T = delaunay(X);

returns the following error message.
qhull input error: can not scale last coordinate. Input is
cocircular
   or cospherical. Use option 'Qz' to add a point at infinity.

The error message indicates that you should add 'Qz' to the default Qhull options.
tri = delaunay(x,y,{'Qt','Qbb','Qc','Qz'})

tri =

     3     2     1
     3     4     1
Algorithm

delaunay is based on Qhull [1]. For information about Qhull, see http://www.qhull.org/. For copyright information, see http://www.qhull.org/COPYING.txt.

Posted in 未分类 | Leave a comment

关于char** 和 const char**

请参看下面代码:

foo(const char **p){}

main(int argc, char **argv)
{
       foo(argv);   //error
}

在vc6.0中会报以下错误:
error C2664: 'foo' : cannot convert parameter 1 from 'char ** ' to 'const char ** '

很多人都认为实参char* s和形参const char*p 应该是相容的,因为标准库中很多字符串函数都是这么用的,为什么实参char** s 和形参 const char**p就不形容呢 ?

分析:

ANSI C 标准第6.3.2.2节中说明中有一句话: 每一个实参应该具有自己的类型,这样它的值可以赋值给与它所对应的形参类型的对象(该对象的类型不能含有限定符)。由此可知参数传值类似于赋值。参看标准中关于简单赋值部分,第6.3.16.1节 有描述:要使上述的赋值形式合法,必须满足下列条件之一:两个操作数都是指向有限定符或无限定符的相容类型的指针,左边的指针所指向的类型必须具有右边指针所指向类型的全部限定符。

看下面代码:
     char *cp;
     const char ** ccp;
     ccp = cp;

1)  左操作数ccp是指向 有const限定符的char 类型 的指针。
2)  右操作数cp 是指向  无限定符的char 类型 的指针。
3)  显然char类型和char类型是相容的。ccp 所指向的类型具有cp所指向类型的限定符(无) 和自身的限定符const. 所以 ccp = cp 是合法的,相反 cp = ccp就会警告。另外 const char** 是一个没有限定符的指针类型, 它的类型是"指向有const限定符的char类型的指针的指针"。
对于char ** 和 const char ** 都是没有限定符的指针类型,但它们所指向的类型不一样( 前者指向char*, 后者指向 const char*), 因此它们是不相容的。

 

转载自abccd的博客,再次鸣谢!

Posted in C/C++ | Leave a comment

OpenCV统计应用-PCA主成分分析【转】

本文转载自 webuserzhy's Blog

在图形识别方面,主成分分析(Principal Comonents Analysis,PCA)算是比较快速而且又准确的方式之一,它可以对抗图形平移旋转的事件发生,并且藉由主要特征(主成分)投影过后的数据做数据的比对,在多个特征信息里面,取最主要的K个,做为它的特征依据,在这边拿前面共变量矩阵的数据来做沿用,主成分分析使用的方法为计算共变量矩阵,在加上计算共变量矩阵的特征值及特征向量,将特征值以及所对应的特征向量排序之后,取前面主要K个特征向量当做主要特征,而OpenCV也可以对高维度的向量进行主成分分析的计算。

1.5 3.0 1.2 2.1 3.1 1.3 2.0 1.0 0.5 1.0
y 2.3 1.7 2.9 2.2 3.1 2.7 1.7 2.0 0.6 0.9

数据原始的分布情况,红点代表着它的平均数

image

将坐标系位移,以红点为主要的原点

image

计算共变量矩阵以及共变量的特征值以及特征向量,将特征向量排序后投影回原始数据的结果的结果,也就是说,对照上面的图片,EigenVector的作用是找到主轴后,将原本的坐标系做旋转了

image

再来就是对它做投影,也就是降低维度的动作,将Y轴的数据全部归零,投影在X轴上

image

投影完之后,在将它转回原本的坐标系

image

PCA主成分分析,与线性回归有异曲同工之妙,也就是说,这条投影过后的直线,可以称做回归线,当它在做主轴旋转的时候,所投影的结果为最小均方误,在将它转置回来的时候,就形成了一条回归直线了
OpenCV的PCA输入必须要是单信道32位浮点数格式或是单信道64位浮点数格式的,参数为CV_32FC1或是CV_64FC1,程序写法如下

PCA程序实作
#include <cv.h>
#include <highgui.h>
#include <stdio.h>
#include <stdlib.h>
float Coordinates[20]={1.5,2.3,
3.0,1.7,
1.2,2.9,
2.1,2.2,
3.1,3.1,
1.3,2.7,
2.0,1.7,
1.0,2.0,
0.5,0.6,
1.0,0.9};
void PrintMatrix(CvMat *Matrix,int Rows,int Cols);
int main()
{
    CvMat *Vector1;
    CvMat *AvgVector;
    CvMat *EigenValue_Row;
    CvMat *EigenVector;
    Vector1=cvCreateMat(10,2,CV_32FC1);
    cvSetData(Vector1,Coordinates,Vector1->step);
    AvgVector=cvCreateMat(1,2,CV_32FC1);
    EigenValue_Row=cvCreateMat(2,1,CV_32FC1);
    EigenVector=cvCreateMat(2,2,CV_32FC1);
    cvCalcPCA(Vector1,AvgVector,EigenValue_Row,EigenVector,CV_PCA_DATA_AS_ROW);
    printf("Original Data:\n&quot ;) ;
    PrintMatrix(Vector1,10,2);
    printf("==========\n&quot ;) ;
    PrintMatrix(AvgVector,1,2);
    printf("\nEigne Value:\n&quot ;) ;
    PrintMatrix(EigenValue_Row,2,1);
    printf("\nEigne Vector:\n&quot ;) ;
    PrintMatrix(EigenVector,2,2);
    system("pause&quot ;) ;
}
void PrintMatrix(CvMat *Matrix,int Rows,int Cols)
{
for(int i=0;i<Rows;i++)
    {
for(int j=0;j<Cols;j++)
        {
            printf("%.2f ",cvGet2D(Matrix,i,j).val[0]);
        }
        printf("\n&quot ;) ;
    }
}

细节分析

    这部份是把平均数,共变量矩阵,以及特征值及特征向量都计算出来了,全部都包在cvCalcPCA()的函式里面,因此可以不必特地的用cvCalcCovarMatrix()求得共变量矩阵,也不需要再由共变量矩阵套用cvEigenVV()求得它的EigenValue以及EigenVector了,所以说,cvCalcPCA()=cvCalcCovarMatrix()+cvEigenVV(),不仅如此,cvCalcPCA()使用上更是灵活,当向量的维度数目比输入的数据大的时候(例如Eigenface),它的共变量矩阵就会自动转成CV_COVAR_SCRAMBLED,而当输入数据量比向量维度大的时候,它亦会自动转成CV_COVAR_NORMAL的形态,而OpenCV也提供了计算投影量cvProjectPCA(),以及反向投影的函式cvBackProjectPCA(),cvCalcPCA()的计算结果如下

    详细计算方法可以参考"OpenCV统计应用-共变量矩阵"以及"OpenCV线性代数-cvEigenVV实作"这两篇,cvCalcPCA()第一个自变量为输入目标要计算的向量,整合在CvMat数据结构里,第二个自变量为空的平均数向量,第三个自变量为输出排序后的EigenValue,以列(Rows)为主的数值,第四个自变量为排序后的EigenVector,第五个自变量为cvCalcPCA()的参数,它的参数公式如下
#define CV_PCA_DATA_AS_ROW 0
#define CV_PCA_DATA_AS_COL 1
#define CV_PCA_USE_AVG 2
分别代表以列为主,以栏为主的参数设定,以及使用自己定义的平均数,CV_PCA_DATA_AS_ROW与CV_PCA_DATA_AS_COL的参数不可以同时使用,而对于主成分分析的EigenVector对原始数据投影的程序范例如下
PCA特征向量投影

cvProjectPCA(),它的公式定义如下

因此,它所呈现的结果就是将坐标旋转后的特征向量投影,cvProjectPCA()的地一个自变量为输入原始向量数据,第二个自变量为输入空的(或是以设定好的)平均数向量,第三个自变量为输入以排序好的特征向量EigenVector,第四个自变量为输出目标投影向量,而投影之外,OpenCV还可以给他做反向投影回原始的数据

PCA反向投影
#include <cv.h>
#include <highgui.h>
#include <stdio.h>
#include <stdlib.h>
float Coordinates[20]={1.5,2.3,
3.0,1.7,
1.2,2.9,
2.1,2.2,
3.1,3.1,
1.3,2.7,
2.0,1.7,
1.0,2.0,
0.5,0.6,
1.0,0.9};
int main()
{
    CvMat *Vector1;
    CvMat *AvgVector;
    IplImage *Image1=cvCreateImage(cvSize(450,450),IPL_DEPTH_8U,3);
    Image1->origin=1;
    Vector1=cvCreateMat(10,2,CV_32FC1);
    cvSetData(Vector1,Coordinates,Vector1->step);
    AvgVector=cvCreateMat(1,2,CV_32FC1);
    CvMat *EigenValue_Row=cvCreateMat(2,1,CV_32FC1);
    CvMat *EigenVector=cvCreateMat(2,2,CV_32FC1);
    cvCalcPCA(Vector1,AvgVector,EigenValue_Row,EigenVector,CV_PCA_DATA_AS_ROW);
    cvProjectPCA(Vector1,AvgVector,EigenVector,Vector1);
    cvBackProjectPCA(Vector1,AvgVector,EigenVector,Vector1);
    printf("Back Project Original Data:\n&quot ;) ;
for(int i=0;i<10;i++)
    {
        printf("%.2f ",cvGetReal2D(Vector1,i,0));
       printf("%.2f ",cvGetReal2D(Vector1,i,1));
        cvCircle(Image1,cvPoint((int)(cvGetReal2D(Vector1,i,0)*100),(int)(cvGetReal2D(Vector1,i,1)*100)),0,CV_RGB(0,0,255),10,CV_AA,0);
        printf("\n&quot ;) ;
    }
    cvCircle(Image1,cvPoint((int)((cvGetReal2D(AvgVector,0,0))*100),(int)((cvGetReal2D(AvgVector,0,1))*100)),0,CV_RGB(255,0,0),10,CV_AA,0);
    printf("==========\n&quot ;) ;
    printf("%.2f ",cvGetReal2D(AvgVector,0,0));
    printf("%.2f ",cvGetReal2D(AvgVector,0,1));
    cvNamedWindow("Coordinates",1);
    cvShowImage("Coordinates",Image1);
    cvWaitKey(0);
}
执行结果:

而反向投影,只不过是将投影向量还原成原始数据罢了,可以用来做为新进数据反向投影后用来比对的步骤,以下是它的计算公式推导

cvBackProjectPCA()的自变量输入与cvProjectPCA()是一样的,只不过是里面的计算公式不同,其实也只是cvProjectPCA()的反运算

cvCalcPCA()
计算多笔高维度数据的主要特征值及特征向量,先自动判断维度大于数据量或维度小于数据量来选择共变量矩阵的样式,在由共变量矩阵求得已排序后的特征值及特征向量,cvCalcPCA()函式的参数为CV_PCA_DATA_AS_ROW以列为主的资料排列,CV_PCA_DATA_AS_COL以行为主的数据排列,CV_PCA_USE_AVG自行定义平均数数据,CV_PCA_DATA_AS_ROW以及CV_PCA_DATA_AS_COL不可以同时使用,cvCalcPCA()的第一个自变量为输入CvMat维度向量数据结构,第二个自变量为输入空的CvMat平均数向量数据结构(或是自行定义平均数向量),第三个自变量为输入以列为主的特征值CvMat数据结构,第四个自变量为输入特征向量CvMat数据结构,第四个自变量为输入cvCalcPCA()函式的参数
cvCalcPCA(输入目标向量数据CvMat数据结构,输入或输出向量平均数CvMat数据结构,输入以列为主EigenValue的CvMat数据结构,输入EigenVector的CvMat数据结构,目标参数或代号)
cvProjectPCA()

将计算主成分分析的结果做投影的运算,主要作用是将最具意义的k个EgienVector与位移后的原始资料做矩阵乘法,投影过后的结果将会降低维度,cvProjectPCA()第一个自变量为输入CvMat数据结构原始向量数据数据,第二个自变量为输入CvMat数据结构平均数向量,第三个自变量为输入要降维的特征向量,第四个自变量为输出CvMat数据结构原始数据投影的结果
cvProjectPCA(输入CvMat原始向量数据数据结构,输入CvMat原始向量平均数数据结构,输入CvMat降低维度的特征向量数据结构,输出CvMat投影向量数据结构)

cvBackProjectPCA()
将投影向量转回原始向量维度坐标系,cvProjectPCA()第一个自变量为输入CvMat数据结构投影向量数据,第二个自变量为输入CvMat数据结构平均数向量,第三个自变量为输入特征向量,第四个自变量为输出CvMat数据结构反向投影的结果
cvProjectPCA(输入CvMat投影向量数据结构,输入CvMat原始向量平均数数据结构,输入CvMat特征向量数据结构,输出CvMat反向投影数据结构)

Posted in Computer Vision, General Development | Leave a comment

BWLABEL in Matlab

    BWLABEL Label connected components in 2-D binary image.
    L = BWLABEL(BW,N) returns a matrix L, of the same size as BW, containing
    labels for the connected components in BW. N can have a value of either
    4 or 8, where 4 specifies 4-connected objects and 8 specifies
    8-connected objects; if the argument is omitted, it defaults to 8.
    The elements of L are integer values greater than or equal to 0.  The
    pixels labeled 0 are the background.  The pixels labeled 1 make up one
    object, the pixels labeled 2 make up a second object, and so on.
    [L,NUM] = BWLABEL(BW,N) returns in NUM the number of connected objects
    found in BW.
    Note: Comparing BWLABEL and BWLABELN
    ------------------------------------
    BWLABEL supports 2-D inputs only, whereas BWLABELN support any
    input dimension.  In some cases you might prefer to use BWLABELN even
    for 2-D problems because it can be faster.  If you have a 2-D input
    whose objects are relatively "thick" in the vertical direction,
    BWLABEL will probably be faster; otherwise BWLABELN will probably be
    faster.
    Class Support
    -------------
    BW can be logical or numeric, and it must be real, 2-D, and
    nonsparse.  L is double.
    Example
    -------

BW = logical([1 1 1 0 0 0 0 0
                      1 1 1 0 1 1 0 0
                      1 1 1 0 1 1 0 0
                      1 1 1 0 0 0 1 0
                      1 1 1 0 0 0 1 0
                      1 1 1 0 0 0 1 0
                      1 1 1 0 0 1 1 0
                      1 1 1 0 0 0 0 0]);
        L = bwlabel(BW,4);
        [r,c] = find(L == 2);
    See also bwareaopen, bweuler, bwlabeln, bwselect, label2rgb.

    Reference page in Help browser
       doc bwlabel

Posted in General Development, Matlab | Leave a comment

关于23种设计模式的有趣见解

转自diborve的专栏

创建型模式

1、FACTORY(工厂模式)

—追MM少不了请吃饭了,麦当劳的鸡翅和肯德基的鸡翅都是MM爱吃的东西,虽然口味有所不同,但不管你带MM去麦当劳或肯德基,只管向服务员说“来四个鸡翅”就行了。麦当劳和肯德基就是生产鸡翅的Factory

客户类和工厂类分开。消费者任何时候需要某种产品,只需向工厂请求即可。消费者无须修改就可以接纳新产品。缺点是当产品修改时,工厂类也要做相应的修改。如:如何创建及如何向客户端提供。

2、BUILDER(建造模式)

MM最爱听的就是“我爱你”这句话了,见到不同地方的MM,要能够用她们的方言跟她说这句话哦,我有一个多种语言翻译机,上面每种语言都有一个按键,见到MM我只要按对应的键,它就能够用相应的语言说出“我爱你”这句话了,国外的MM也可以轻松搞掂,这就是我的“我爱你 ”builder。(这一定比美军在伊拉克用的翻译机好卖)

将产品的内部表象和产品的生成过程分割开来,从而使一个建造过程生成具有不同的内部表象的产品对象。建造模式使得产品内部表象可以独立的变化,客户不必知道产品内部组成的细节。建造模式可以强制实行一种分步骤进行的建造过程。

3、FACTORY METHOD(工厂方法模式)

—请MM去麦当劳吃汉堡,不同的MM有不同的口味,要每个都记住是一件烦人的事情,我一般采用Factory Method模式,带着MM到服务员那儿,说“要一个汉堡”,具体要什么样的汉堡呢,让MM直接跟服务员说就行了。

核心工厂类不再负责所有产品的创建,而是将具体创建的工作交给子类去做,成为一个抽象工厂角色,仅负责给出具体工厂类必须实现的接口,而不接触哪一个产品类应当被实例化这种细节。

4、PROTOTYPE(原始模型模式)

—跟MM用QQ聊天,一定要说些深情的话语了,我搜集了好多肉麻的情话,需要时只要copy出来放到QQ里面就行了,这就是我的情话prototype了。(100块钱一份,你要不要)

通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的方法创建出更多同类型的对象。原始模型模式允许动态的增加或减少产品类,产品类不需要非得有任何事先确定的等级结构,原始模型模式适用于任何的等级结构。缺点是每一个类都必须配备一个克隆方法。

5、SINGLETON(单例模式)

—俺有6个漂亮的老婆,她们的老公都是我,我就是我们家里的老公Sigleton,她们只要说道“老公”,都是指的同一个人,那就是我(刚才做了个梦啦,哪有这么好的事)

单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例单例模式。单例模式只应在有真正的“单一实例”的需求时才可使用。

结构型模式

6、ADAPTER(适配器(变压器)模式)

—在朋友聚会上碰到了一个美女Sarah,从香港来的,可我不会说粤语,她不会说普通话,只好求助于我的朋友kent了,他作为我和Sarah之间的Adapter,让我和Sarah可以相互交谈了(也不知道他会不会耍我)

把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口原因不匹配而无法一起工作的两个类能够一起工作。适配类可以根据参数返还一个合适的实例给客户端。

7、BRIDGE(桥梁模式)

—早上碰到MM,要说早上好,晚上碰到MM,要说晚上好;碰到MM穿了件新衣服,要说你的衣服好漂亮哦,碰到MM新做的发型,要说你的头发好漂亮哦。不要问我“早上碰到MM新做了个发型怎么说”这种问题,自己用BRIDGE组合一下不就行了

将抽象化与实现化脱耦,使得二者可以独立的变化,也就是说将他们之间的强关联变成弱关联,也就是指在一个软件系统的抽象化和实现化之间使用组合/聚合关系而不是继承关系,从而使两者可以独立的变化。

8、COMPOSITE(合成模式)

Mary今天过生日。“我过生日,你要送我一件礼物。”“嗯,好吧,去商店,你自己挑。”“这件T恤挺漂亮,买,这条裙子好看,买,这个包也不错,买。”“喂,买了三件了呀,我只答应送一件礼物的哦。”“什么呀,T恤加裙子加包包,正好配成一套呀,小姐,麻烦你包起来。 ”“……”,MM都会用Composite模式了,你会了没有?

合成模式将对象组织到树结构中,可以用来描述整体与部分的关系。合成模式就是一个处理对象的树结构的模式。合成模式把部分与整体的关系用树结构表示出来。合成模式使得客户端把一个个单独的成分对象和由他们复合而成的合成对象同等看待。

9、DECORATOR(装饰模式)

Mary过完轮到Sarly过生日,还是不要叫她自己挑了,不然这个月伙食费肯定玩完,拿出我去年在华山顶上照的照片,在背面写上“最好的的礼物,就是爱你的Fita”,再到街上礼品店买了个像框(卖礼品的MM也很漂亮哦),再找隔壁搞美术设计的Mike设计了一个漂亮的盒子装起来……,我们都是Decorator,最终都在修饰我这个人呀,怎么样,看懂了吗?

装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案,提供比继承更多的灵活性。动态给一个对象增加功能,这些功能可以再动态的撤消。增加由一些基本功能的排列组合而产生的非常大量的功能。

10、FACADE(门面模式)

我有一个专业的Nikon相机,我就喜欢自己手动调光圈、快门,这样照出来的照片才专业,但MM可不懂这些,教了半天也不会。幸好相机有Facade设计模式,把相机调整到自动档,只要对准目标按快门就行了,一切由相机自动调整,这样MM也可以用这个相机给我拍张照片了。

外部与一个子系统的通信必须通过一个统一的门面对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。每一个子系统只有一个门面类,而且此门面类只有一个实例,也就是说它是一个单例模式。但整个系统可以有多个门面类。

11、FLYWEIGHT(享元模式)

每天跟MM发短信,手指都累死了,最近买了个新手机,可以把一些常用的句子存在手机里,要用的时候,直接拿出来,在前面加上MM的名字就可以发送了,再不用一个字一个字敲了。共享的句子就是Flyweight,MM的名字就是提取出来的外部特征,根据上下文情况使用。

FLYWEIGHT在拳击比赛中指最轻量级。享元模式以共享的方式高效的支持大量的细粒度对象。享元模式能做到共享的关键是区分内蕴状态和外蕴状态。内蕴状态存储在享元内部,不会随环境的改变而有所不同。外蕴状态是随环境的改变而改变的。外蕴状态不能影响内蕴状态,它们是相互独立的。将可以共享的状态和不可以共享的状态从常规类中区分开来,将不可以共享的状态从类里剔除出去。客户端不可以直接创建被共享的对象,而应当使用一个工厂对象负责创建被共享的对象。享元模式大幅度的降低内存中对象的数量。

12、PROXY(代理模式)

跟MM在网上聊天,一开头总是“hi,你好”,“你从哪儿来呀?”“你多大了?”“身高多少呀?”这些话,真烦人,写个程序做为我的Proxy吧,凡是接收到这些话都设置好了自动的回答,接收到其他的话时再通知我回答,怎么样,酷吧。

代理模式给某一个对象提供一个代理对象,并由代理对象控制对源对象的引用。代理就是一个人或一个机构代表另一个人或者一个机构采取行动。某些情况下,客户不想或者不能够直接引用一个对象,代理对象可以在客户和目标对象直接起到中介的作用。客户端分辨不出代理主题对象与真实主题对象。代理模式可以并不知道真正的被代理对象,而仅仅持有一个被代理对象的接口,这时候代理对象不能够创建被代理对象,被代理对象必须有系统的其他角色代为创建并传入。

行为模式

13、CHAIN OF RESPONSIBLEITY(责任链模式)

晚上去上英语课,为了好开溜坐到了最后一排,哇,前面坐了好几个漂亮的MM哎,找张纸条,写上“Hi,可以做我的女朋友吗?如果不愿意请向前传”,纸条就一个接一个的传上去了,糟糕,传到第一排的MM把纸条传给老师了,听说是个老处半夜凉初透女呀,快跑!

在责任链模式中,很多对象由每一个对象对其下家的引用而接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。客户并不知道链上的哪一个对象最终处理这个请求,系统可以在不影响客户端的情况下动态的重新组织链和分配责任。处理者有两个选择:承担责任或者把责任推给下家。一个请求可以最终不被任何接收端对象所接受。

14、COMMAND(命令模式)

俺有一个MM家里管得特别严,没法见面,只好借助于她弟弟在我们俩之间传送信息,她对我有什么指示,就写一张纸条让她弟弟带给我。这不,她弟弟又传送过来一个COMMAND,为了感谢他,我请他吃了碗杂酱面,哪知道他说:“我同时给我姐姐三个男朋友送COMMAND,就数你最小气,才请我吃面。”, :-(

命令模式把一个请求或者操作封装到一个对象中。命令模式把发出命令的责任和执行命令的责任分割开,委派给不同的对象。命令模式允许请求的一方和发送的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否执行,何时被执行以及是怎么被执行的。系统支持命令的撤消。

15、INTERPRETER(解释器模式)

俺有一个《泡MM真经》,上面有各种泡MM的攻略,比如说去吃西餐的步骤、去看电影的方法等等,跟MM约会时,只要做一个Interpreter,照着上面的脚本执行就可以了。

给定一个语言后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器。客户端可以使用这个解释器来解释这个语言中的句子。解释器模式将描述怎样在有了一个简单的文法后,使用模式设计解释这些语句。在解释器模式里面提到的语言是指任何解释器对象能够解释的任何组合。在解释器模式中需要定义一个代表文法的命令类的等级结构,也就是一系列的组合规则。每一个命令对象都有一个解释方法,代表对命令对象的解释。命令对象的等级结构中的对象的任何排列组合都是一个语言。

16、ITERATOR(迭代子模式)

我爱上了Mary,不顾一切的向她求婚。

Mary:“想要我跟你结婚,得答应我的条件”

我:“什么条件我都答应,你说吧”

Mary:“我看上了那个一克拉的钻石”

我:“我买,我买,还有吗?”

Mary:“我看上了湖边的那栋别墅”

我:“我买,我买,还有吗?”

Mary:“你的小弟弟必须要有50cm长”

我脑袋嗡的一声,坐在椅子上,一咬牙:“我剪,我剪,还有吗?”

……

迭代子模式可以顺序访问一个聚集中的元素而不必暴露聚集的内部表象。多个对象聚在一起形成的总体称之为聚集,聚集对象是能够包容一组对象的容器对象。迭代子模式将迭代逻辑封装到一个独立的子对象中,从而与聚集本身隔开。迭代子模式简化了聚集的界面。每一个聚集对象都可以有一个或一个以上的迭代子对象,每一个迭代子的迭代状态可以是彼此独立的。迭代算法可以独立于聚集角色变化。

17、MEDIATOR(调停者模式)

四个MM打麻将,相互之间谁应该给谁多少钱算不清楚了,幸亏当时我在旁边,按照各自的筹码数算钱,赚了钱的从我这里拿,赔了钱的也付给我,一切就OK啦,俺得到了四个MM的电话。

调停者模式包装了一系列对象相互作用的方式,使得这些对象不必相互明显作用。从而使他们可以松散偶合。当某些对象之间的作用发生改变时,不会立即影响其他的一些对象之间的作用。保证这些作用可以彼此独立的变化。调停者模式将多对多的相互作用转化为一对多的相互作用。调停者模式将对象的行为和协作抽象化,把对象在小尺度的行为上与其他对象的相互作用分开处理。

18、MEMENTO(备忘录模式)

同时跟几个MM聊天时,一定要记清楚刚才跟MM说了些什么话,不然MM发现了会不高兴的哦,幸亏我有个备忘录,刚才与哪个MM说了什么话我都拷贝一份放到备忘录里面保存,这样可以随时察看以前的记录啦。

备忘录对象是一个用来存储另外一个对象内部状态的快照的对象。备忘录模式的用意是在不破坏封装的条件下,将一个对象的状态捉住,并外部化,存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态。

19、OBSERVER(观察者模式)

想知道咱们公司最新MM情报吗?加入公司的MM情报邮件组就行了,tom负责搜集情报,他发现的新情报不用一个一个通知我们,直接发布给邮件组,我们作为订阅者(观察者)就可以及时收到情报啦

观察者模式定义了一种一队多的依赖关系,让多个观察者对象同时监瑞脑消金兽听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使他们能够自动更新自己。

20、STATE(状态模式)

跟MM交往时,一定要注意她的状态哦,在不同的状态时她的行为会有不同,比如你约她今天晚上去看电影,对你没兴趣的MM就会说“ 有事情啦”,对你不讨厌但还没喜欢上的MM就会说“好啊,不过可以带上我同事么?”,已经喜欢上你的MM就会说“几点钟?看完电影再去泡吧怎么样?”,当然你看电影过程中表现良好的话,也可以把MM的状态从不讨厌不喜欢变成喜欢哦。

状态模式允许一个对象在其内部状态改变的时候改变行为。这个对象看上去象是改变了它的类一样。状态模式把所研究的对象的行为包装在不同的状态对象里,每一个状态对象都属于一个抽象状态类的一个子类。状态模式的意图是让一个对象在其内部状态改变的时候,其行为也随之改变。状态模式需要对每一个系统可能取得的状态创立一个状态类的子类。当系统的状态变化时,系统便改变所选的子类。 

21、STRATEGY(策略模式)

跟不同类型的MM约会,要用不同的策略,有的请电影比较好,有的则去吃小吃效果不错,有的去海边浪漫最合适,单目的都是为了得到MM的芳心,我的追MM锦囊中有好多Strategy哦。

策略模式针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。策略模式把行为和环境分开。环境类负责维持和查询行为类,各种算法在具体的策略类中提供。由于算法和环境独立开来,算法的增减,修改都不会影响到环境和客户端。

22、TEMPLATE METHOD(模板方法模式)

看过《如何说服女生上帘卷西风床》这部经典文章吗?女生从认识到上帘卷西风床的不变的步骤分为巧遇、打破僵局、展开追求、接吻、前戏、动手、爱抚、进去八大步骤(Template method),但每个步骤针对不同的情况,都有不一样的做法,这就要看你随机应变啦(具体实现);

模板方法模式准备一个抽象类,将部分逻辑以具体方法以及具体构造子的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。先制定一个顶级逻辑框架,而将逻辑的细节留给具体的子类去实现。

23、VISITOR(访问者模式)

情人节到了,要给每个MM送一束鲜花和一张卡片,可是每个MM送的花都要针对她个人的特点,每张卡片也要根据个人的特点来挑,我一个人哪搞得清楚,还是找花店老板和礼品店老板做一下Visitor,让花店老板根据MM的特点选一束花,让礼品店老板也根据每个人特点选一张卡,这样就轻松多了;

访问者模式的目的是封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改的话,接受这个操作的数据结构可以保持不变。访问者模式适用于数据结构相对未定的系统,它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由的演化。访问者模式使得增加新的操作变的很容易,就是增加一个新的访问者类。访问者模式将有关的行为集中到一个访问者对象中,而不是分散到一个个的节点类中。当使用访问者模式时,要将尽可能多的对象浏览逻辑放在访问者类中,而不是放到它的子类中。访问者模式可以跨过几个类的等级结构访问属于不同的等级结构的成员类。

Posted in 未分类 | 2 Comments

Hidden Markov Model--Viterbi 算法

Viterbi算法是一种动态规划算法,用来寻找由观测信息产生(Observed Event)的最可能隐状态序列(Viterbi路径),这种方法通常用在隐马尔可夫模型中。向前算法是一个类似的算法,用来计算一串观测事件发生的概率。这些算法都属于信息论的范畴。

这个算法做一连串的假设。首先,观测事件和隐事件必须处于序列中。这个序列通常是关于时间的。第二,这两个序列需要对应,一个观测事件的实例必须与一个隐事件相关联。第三,计算在特定时间点t的最可能隐序列必须只依赖于位于t的观测事件,和t-1处的最可能序列。这些假设在一阶隐马尔可夫模型中都要被满足。

Viterbi路径和Viterbi算法同时遵循寻找单一最可能观测解释的相关动态规划算法。例如,在统计分析中的动态规划算法能应用于寻找一个字符串的单个最相似上下文无关推导,即“Viterbi推导”。

Viterbi算法是由Andrew Viterbi 在1967年提出的,是一种用于有噪声的数据链路中错误纠正的模型,并广泛应用在卷积码的解码中,例如CDMA/GSM数字蜂窝,拨号调制解调器,卫星通信,深空通信和802.11无线局域网等。现在也广泛的应用在语言理解,关键词匹配,计算机语言学,生物信息学等。例如,在语音理解中,听觉信号被认为是观测事件的序列,文字串被认为是“潜在的原因”。Viterbi算法能够找到对应听觉信号的最可能文字序列。

概要

前面提到的假设可以被如下概括。Viterbi算法在一个状态机的假设上做操作。也就是说,在任何时间系统被抽象为一些状态。这些状态是有限的,尽管很大。每个状态被表示为一个节点。多个状态的序列(路径)往往都能产生同一个给定的状态,但其中只有一条是最可能产生这个状态的,被称作“生存路径”。这是一个最基础的假设,因为这个算法会检测所有的可能路径并只保留一个最可能的路径。这种策略并不需要计算所有的路径,只需要一个状态一个路径而已。

第二个关键的假设是前一个状态到一个新状态的转移被一个递增的度量描述,通常是一个数字。这种转移是从实践中计算而来的。第三个假设是事件在一个路径上是累加的。所以整个算法的关键是计算每个状态的数值。当发生了一个事件,算法结合上一个可能状态与转换产生的增量度量,并从中选择一个最优的,据此来检测向前的新状态。增量度量由事件触发,并由旧状态与新状态间的转换决定。例如,在数据交换中,可能发生一半的符号由奇状态转换,而另一半由偶状态开始转换。同时,在很多例子中,状态转换图是不连续的。一个简单的例子,一个汽车有三个状态,向前,停止和向后,状态从向前倒向后是不允许的。他必须先进入停止状态。在计算出增量度量和和状态度量后,只有最好的幸存,而其他的被舍弃。这种基础算法有一个改进,允许向前搜索和向后搜索。

路径历史必须被储存。在一些实例中,搜索历史是完整的,因为状态机在编码端是从一个已知的状态开始的,而且有充足的空间来维持这些路径。再另一些例子中,需要一个在资源有限条件下的解决方案:一个例子是卷积编码,解码器必须在性能能允许的前提下尽量缩短对历史的记录。尽管Viterbi算法是非常高效的,而且有很多降低计算压力的改进,但它对空间的要求仍然趋向常量。

image 

此处接上文

实现

Alice知道Bob三天来的活动,第一天出去散步,第二天去购物,第三天清洁了公寓。Alice有两个问题:观测序列的整体概率是什么样的?这几天的天气是什么,最能给出观测序列这样的结果。第一个问题由向前算法计算;第二个问题由Viterbi算法计算。这两个算法在结构上是相似的,事实上,他们是同一种抽象算法的两个不同实例,所以他们可以在一个函数中实现。

def forward_viterbi(obs, states, start_p, trans_p, emit_p):
   T = {}
   for state in states:
       ##          prob.           V. path  V. prob.
       T[state] = (start_p[state], [state], start_p[state])
   for output in obs:
       U = {}
       for next_state in states:
           total = 0
           argmax = None
           valmax = 0
           for source_state in states:
               (prob, v_path, v_prob) = T[source_state]
               p = emit_p[source_state][output] * trans_p[source_state][next_state]
               prob *= p
               v_prob *= p
               total += prob
               if v_prob > valmax:
                   argmax = v_path + [next_state]
                   valmax = v_prob
           U[next_state] = (total, argmax, valmax)
       T = U
   ## apply sum/max to the final states:
   total = 0
   argmax = None
   valmax = 0
   for state in states:
       (prob, v_path, v_prob) = T[state]
       total += prob
       if v_prob > valmax:
           argmax = v_path
           valmax = v_prob
   return (total, argmax, valmax)

函数“forward_viterbi”需要如下几个输入参数,“obs”是观测序列,比如[`walk`,`shop`,`clean`];“states”是隐状态的集合;“start_p”是初始的概率,“trans_p”是转移概率;“emit_p”是产生概率。

这个算法在位图T和U上做操作,每个都是一个从一个状态到一个三元组(prob,v_path,v_prob)的映射,其中prob表示从开始到现在这个状态的概率之和;v_path是到当前状态的Viterbi路径,v_prob是到当前状态的Viterbi路径的概率。位图T保存保存一个给定时间点t的信息,主循环结构U,保存t+1的相似信息。因为马尔可夫性,任何早于时间t的信息是不需要的。

算法初始化T为开始的概率:一个状态的总概率是这个状态开始的概率;到一个开始状态的Viterbi路径是一个单独的路径,并只包含这个状态;Viterbi路径的概率与起始概率是相同的。

主循环考量“obs”中的观测值序列。T包含正确的信息但排除当前观测的点。算法继续计算下一个可能状态的三元组(prob,v_path,v_prob)。一个给定状态的总概率,total,为所有到达这个状态的概率的和。更确切的说,算法迭代所有可能的源状态。对每一个愿状态,T保存到某各状态所有路径的概率和。这个概率然后乘以当前观测的产生概率和从当前状态转换到下一状态的转换概率。产生的结果“prob”加入“total”中。“Viterbi”路径的概率用一种类似的方法求得,但用一个离散的最大值代替了累加所有的路径。起始时,最大值valmax为零。对每个原状态,“Viterbi”路径的概率是已知的。这也由产生概率,转移概率的乘积获得,如果大于现有值,就取代“valmax”。通过拓展从下一状态指向当前状态的“Viterbi”路径,“Viterbi”路径被当作对应最大值的“argmax”来计算的。在这种模式下得到的三元组(prob,v_path,v_prob)被储存在U中,一旦得到所有可能的下一状态的U,就取代T,然后保证在迭代的最后循环的不变量得到该值。

在最后,使用另一个总结/最大化的过程(当然,这一步也可以在主循环的内部进行,添加一个条件语句并在最后一次迭代中执行这一过程)。

在执行的例子中,算法的使用方法如下:

def example():
    return forward_viterbi(observations,
                           states,
                           start_probability,
                           transition_probability,
                           emission_probability)
print example()

这个表明[`walk`,`shop`,`clean`]的概率和为0.033612,‘Viterbi’路径为[`Sunny`,`Rainy`,`Rainy`,`Rainy`],概率为0.009408. ‘Viterbi’路径包含四个状态,因为第四个观测值是由第第三个状态产生,并转移到第四个状态。换句话说,一个已知的观测动作,当Bob出去散步时天气最可能是晴天;第一天下雨而接着那天也下雨。

在实现这个算法时,需要注意到很多语言用浮点数,因为p是很小的,这可能会造成读取空缓存。一个常用的技巧是取概率的对数并用它贯穿整个计算。这个技巧通常也用在对数系统中。一旦算法结束,正确的结果可以通过指数运算得到。

如果你希望实现这算法,使它能接受任意状态,观测值,和概率值,用下面的代码代替上文的声明部分:

states = []
number_of_states=input('how many states? Please write an integer different from 0 or 1')
index=0
while index<number_of_states:
   states.append(str(raw_input('give a name to the state number'+str(index))))
   index=index+1
observations = [] number_of_observations=input('how many observations? Please write an integer different from 0 or 1') index=0 while index<number_of_observations: observations.append(str(raw_input('give a name to the observation number '+str(index)))) index=index+1
start_probability = {} for state in states: start_probability[state] = input('give a value for the start probability of the state '+state)
transition_probability = {} for initial_state in states: transition_probability[initial_state] = {} for final_state in states: transition_probability[initial_state][final_state]=input('give a value for the transition probability from the state '+initial_state+' to the state '+final_state)
emission_probability = {} for state in states: emission_probability[state]= {} for observation in observations: emission_probability[state][observation]=input('give a value for the emission probability from the state '+state+' to the observation '+observation)
Posted in 未分类 | Leave a comment

学习明史的药方【转】

在某种意义上说,你了解了明朝,你就理解了中国。
一,菜鸟级:

1,《朱元璋传》人民出版社

吴晗著,这是明史研究开创性人物吴晗的代表作。人民社本还是繁体字,大家看起来可能吃力一些,好像百花文艺社有简体字本。明朝是朱元璋一手建立,他制定的一些政策和方针对明朝后来的历史影响非常大。甚至可以说,他治国的一些理念至今都有影响。了解明朝,此书不可不看。(吴晗还有《明史简述》,比较薄的一册,是演讲稿成书,很容易读,对明朝的大致轮廓和特色介绍得比较通俗,值得一观。)

2,《细说明朝》上海人民出版社

台湾史学家黎东方著,上海人民社本。民瑞脑消金兽国时期,黎东方在大后方讲三国,据说是万人空巷,跟现在易中天品三国,有得一比。黎著特色是一节一节讲明代人物和事件,很细,不时也有新的发现,带有演讲的口语色彩。值得收藏。(上海人民社以黎著为基础,推出了整套中国史“细说系列”,都不错。)

3,《正说明朝十六帝》中华书局

中国社科院明史研究室年轻学者许文继和陈时龙合著。此书以《正说清朝十二帝》的姊妹篇推出,细致描绘了明朝十六个皇帝的一生,对他们的性情、事功都有所发明。从书中,我们可以看到,宣宗的隐忧,英宗的仁慈,景帝的功绩,武宗的多情,万历的有为……这些都与我们往常的印象有所不同。易中天在百家讲坛开讲之前,注意到正说历史系列,曾写过一篇书评。我联系他,他表示,《正说明朝十六帝》比《正说清朝十二帝》要写得好。我想原因不难解释,因为这本书是一气呵成,自然流畅,而《十二帝》是从讲稿综合编辑而成。欲了解明朝各个特性不同的皇帝以及他们所处的时代,《正说明朝十六帝》不可不读。

4,《明朝那些事儿》•朱元璋卷 中国友谊出版公司

当年明月著。今年3月天涯网站贴出这个系列时,我就读了一些,感觉写得很灵动,有气势。后来又闹起了纠纷,直到出书,整个过程俨然水到渠成,不能不叹服草根历史的崛起之迅速。对于菜鸟级来说,这是一本最好读的明史。不过,由于作者的知识背景以及网络写作的风格,这本书有些轻飘,还当辅以其他厚重些的读物,才可以修佳节又重阳炼晋级:)(记得以前我读过一本《骇版战国》,风格类似当年明月,很好读,当然也很飘,生不逢时,没在网络引起更大反响,所以只发了1万册。《明朝那些事儿》虽然火,但距离PK易中天,还是很远。)(另有《毛佩琦细解明朝十七帝》和《帝国政界往事•明王朝纪事》两书亦可参读。前书是毛佩琦著,在趣味性上要比当年明月差一些。后书为《帝国政界往事》宋朝卷作者李亚平所著。但这本书作为宋朝卷的后续,明显在结构和文气上远逊于宋朝卷。不可思议的是,依然有很多文化名人推崇这本明朝卷。我的感觉是,这本书没有脱离吴晗《朱元璋传》早期版本《明太祖》的认知水平,借骂朱元璋骂专人比黄花瘦制而已,不足以尽显朱元璋之本来面目。)

二,大虾级

5,《万历十五年》中华书局,三联书店

黄仁宇著。这本书81年在美国、82年在中国问世,自此数十年风行不衰,得益于它发人深思的史观和别具一格的叙述。作者经历跨时代、跨国家的冲突动荡,阅世经验丰富,对明史研究虽然半路出家,但自成体系,书人两得,造就这一难得的神品。如果要推荐读明史的惟一一本书,我就推荐这一本。推荐这本书的人太多了,他们的身份各色各样,有作家,艺术家,企业家,心理学家……我觉得,每一个人都能读出自己的味道。而且,我想,这本书对了解自秦始皇以后的整个中国历史都有非常大的益处。多说一句,这本书对中国历史写作来说,是确立了一个榜样。往后的吴思,李亚平,易中天,等等诸人,无一不坦言受到此书的影响。

6,《十六世纪明代中国之财政与税收》三联书店

黄仁宇著,剖析明代财政制度与税收特色的专著。读起来要费点神,但读进去了,绝对收获非凡。这本书是黄仁宇大历史观的奠基石,从税收角度解释中国王朝统治的特色。

7,《潜规则——中国历史中的真实游戏》云南人民出版社

吴思著。本书不是纯粹讲明史,其中大多篇章是借明史剖析中国传统社会。这本书里以利益为衡量,提出“潜规则”现象。吴思声称黄仁宇讲历史是水只烧到了99度,没到沸点,而潜规则是更进一步,烧开了。其实,黄仁宇是从文化角度来处理明代社会特质的,比如提出明代弊端在于以道德代替法律。吴思只不过更实用一步,进行利益考量,所以要算命价,更提出血酬概念,不好过誉。相比而言,黄仁宇的研究更值得深思,提出了弊端所在,但又不忘给人以借鉴(在《万历十五年和我的大历史观》里,黄仁宇完整解说自己的史观及其参考价值)。当然,《潜规则》这本书还是值得推荐。

8,《南明史》中国青年出版社

北师大教授顾诚著。这本大书我在上大学时读到,自此进一步培养了对明史的兴趣。此书不同于一般东引西列的著作,而是在构建明亡清起这一大变局的双方较量过程中,不时根据考证,提出坚实的看法,比如对郑成功抗清真实目的的解读,使人认识到郑成功被誉为抗清英雄完全是一个误会。前一段时间,电视台在播赵文卓演的《郑成功》,郑成功复台,“可爱”的清朝皇帝下令沿海清兵,不要如往常一样攻击郑部,因为郑成功这次是替大清收复台湾去的。这让我不禁哑然失笑:政治又一次强奸了艺术!我知道,对南明史感兴趣的朋友应该不少,故此郑重推荐这本厚实严谨的《南明史》。

三,大仙级

9,《明史纪事本末》中华书局

将明朝历史分成一些首尾完整的事件进行记叙,便于简明了解历史。

10,《明史》中华书局

张廷玉等修。《明史》是二十四史中前四史之后较好的一种。价值自不待言,但要研读,需要时间和耐心。但《明史》记载不全可信。很多问题都需要参照其他资料进行解读,明史学者在这方面也有很多研究。具体书目就不在这介绍了。呵呵。(中华书局另出有《元明史料笔记丛刊》,收录《水东日记》《万历野获编》《草木子》《今言》等明人笔记,属野史性质,也有史料参考价值。有兴趣的朋友不妨一读。)

 

转自“西丰客人”,在此谢过!

Posted in 转载 | Leave a comment

Hidden Markov Model--Introduction

隐马尔可夫模型(HMM)是一种假设无可观测状态的马尔可夫数学模型。隐马尔可夫模型可以看作是最简单的动态贝叶斯网络。

在传统的马尔可夫模型中,状态是直接可见的,所以状态之间的转移概率是唯一的参数。在隐马尔可夫模型中,状态不是直接可见的,但依赖转台的输出是可见的。每个状态对可能的输出都有一定的概率。所以隐马尔可夫模型的输出序列提供了一些状态的信息。注意形容词“隐”在这里表示状态序列而不是模型的参数;即便是模型参数是已知的,这个模型仍然是“隐”的。

隐马尔可夫模型因为它在有时间参数的模式识别中的广泛应用而为人所知,例如语言,手书识别,手势识别,部分语言标注,音乐评分等等。

隐马尔可夫模型的结构

下面的图表示隐马尔可夫模型的最基本结构。每个椭圆形表示一个随机变量,且能接受一个有限集合中的任意值。随机变量x(t)表示在时间t时的隐状态。箭头表示状态间的相互依赖。

从图表中可知,隐变量x(t)在时间t下的条件概率分布只依赖于隐变量x(t-1):t-2及以前的值是没有影响的。这个性质被称作马尔可夫性。类似的,观测变量y(t)的值只依赖于隐变量x(t)的值。

500px-Hmm_temporal_bayesian_net.svg

一个观测序列的概率

一个观测序列的概率为:

image

长度L由下式给出:

image

其中,x取所有可能的隐节点序列

image

直接计算P(Y)在现实问题中是不现实的,因为可能的隐节点序列通常是非常长的,而且是指数型增加的。但是计算的速度通常能通过“向前算法”和等价的“向后算法”显著提高。

使用隐马尔可夫模型

通常有三种典型的HMM问题:

给定模型的参数,计算一个特定序列输出的概率。这需要对所有可能的状态序列作出总结,但通过向前算法,是可以高效的计算出结果。这也是动态规划的一种形式。

给定模型的参数和一个输出序列,找到最可能产生这种输出的状态序列。这需要在所有可能的状态序列中找到最匹配的一个。通过Viterbi算法,这了问题也能有效解决。

给定一个输出序列或一个这类输出的集合,找到最相似的状态转移集合和输出概率。换句话说,从一个输出序列的数据集中获得HMM参数的极大似然估计。目前还没有有效的算法来准确的解决这类问题,但一种局部的极大相似性可以通过Baum-Welch算法或Baldi-Chauvin算法得到。Baum-Welch算法被称作“向前向后”算法,是最大期望算法的一个特例。

一个实际的例子

假设有两个人,Alice 和Bob,他们彼此居住的非常远。他们每天通过电话谈论自己每天都干了什么。Bob只对三种活动感兴趣,公园中散步,购物和打扫公寓。他每天干什么是由当天的天气独立决定的。Alice没有Bob居住地的天气信息,但她知道一个大概的趋势。基于Bob告诉她的每天自己的活动,Alice试图猜测Bob居住地最可能的天气情况。

Alice相信天气的变化是一个离散的马尔可夫链。它有两个状态,“晴朗”和“下雨”,但她并不能直接观测到,即他们对Alice是隐含的。每一天,Bob会根据当天的天气进行一种活动,包括散步,购物和清理。Bob告诉Alice当天的活动,即为输出的结果。这个系统即为一个隐马尔可夫模型。

Alice知道天气的大概趋势和Bob在各种天气下一般喜欢做什么。换句话讲,HMM的参数是已知的。用Python可做如下定义:

states = ('Rainy', 'Sunny')
observations = ('walk', 'shop', 'clean')
start_probability = {'Rainy': 0.6, 'Sunny': 0.4}
transition_probability = { 'Rainy' : {'Rainy': 0.7, 'Sunny': 0.3}, 'Sunny' : {'Rainy': 0.4, 'Sunny': 0.6}, }
emission_probability = { 'Rainy' : {'walk': 0.1, 'shop': 0.4, 'clean': 0.5}, 'Sunny' : {'walk': 0.6, 'shop': 0.3, 'clean': 0.1}, }

在上面的代码中,start_probability表示Alice认为的当Bob第一次给他打电话时的HMM状态(她所知道的只是平均的天气趋势)。这里的概率分布并不是相等的,而是一组现实的近似值(“下雨”:0.571,“晴朗”:0.429)。transition_probability表示各种天气之间的转换概率,即马尔可夫连的转换概率。在这个例子中,今天下雨明天晴朗的概率只有30%。emission_probability表示在各种天气下Bob做各种动作的概率。如果下雨,清洁公寓的概率为50%;如果晴朗,出去散步的概率是60%。

image

未完待续

Posted in Computer Vision, Data Mining | Leave a comment

直方图均衡化

首先,我们需要获取要处理的图片中所有像素的色彩分布统计,也就是上面的几个通道所作那样。

假设有一张图(我们直接用灰度来表示):

 

100 50 20
20 40 50
100 250 200

统计入下:

20: 2
40: 1
50: 2
100:2
200:1
250:1 

这张图一共有9个像素,我们用比例来表示每种颜色的出现比例:

20: 2 / 9
40: 1 / 9
50: 2 / 9
100:2 / 9
200:1 / 9
250:1 / 9 

由于所有的色彩出现的次数不可能超过图片的总像素,因此,将所有色彩的比例相加也不会超过1(大家已经可以看出正好是1)

最后我们按照从低到高的顺序,把各个色彩的比例进行加权统计,也就是当前点的“权”等于该点的原有比例加上前一个点的“权”,我们得到一个新的统计表:

20:2 / 9
40:3 / 9
50:5 / 9
100:7 / 9
200:8 / 9
250:9 / 9 

最后,根据这个新的统计表,我们来把像素的亮度用一个新的亮度来代替,算法为:

新亮度=该点“权”×255

20: 2 / 9   >>     20 (第一点不动,依然用20)
40: 3 / 9×255=85
50: 5 / 9×255=141
100:7 / 9×255=198
200:8 / 9×255=226
250:9 / 9×255=255 

这时我们得到了新的图:

198 141 20
10 85 141
198 255 226

 

原图中相对出现频率多的部分的宽度变大了。而出现较少的部分则变窄了。

所以,灰度直方图均衡的作用就是把一张图片上出现多的色彩拓展,而把出现少的色彩压缩。

从而得到了更“均衡”的色彩分布。

Posted in Computer Vision | Leave a comment