分类
其它

每个南开人,都曾有过清北梦

每个南开人,都曾有过清北梦。

我曾经的梦想是北大。

但是后来我来了南开,学软件。

我曾以为,我是不幸的。但是现在我感觉,我是幸运的。

物理,数学,通的是万物之理。政法,经济,讲的是天下之秩。能源,机械,搞的是生世之技。唯有程序,算法,聚的是巅峰之智。可以算万物之理,拟天下之秩,行生世之技。

我曾经,一数一式,推演万物之理。我曾愿,一丁一卯,构建生世之技。何曾想过,可以一珠一玑,孕育巅峰之智。有些事情,古人办不到,今人办不到,未来也难有人能办到。但是程序可以,因为算法可以聚集全天下人的智慧,来突破世界的界限。

人的智慧,本身就是一种算法,而世间万物,也皆是算法。同为算法,与人造算法不同的是,自然算法是完美的。而完美的算法即是永恒的,纵然千秋万代,都一步步输出着它该有的结果。永恒的算法所持有的艺术,就是算法的灵魂。一旦算法拥有了灵魂,它就不再是人类可以简单理解的东西了。就像“我从哪里来?”,“我到哪里去?”,而要解决这些问题,就要通晓自然算法的本质,去赋予算法以灵魂。因此,程序是目前人类唯一拥有的可以与世界抗衡的资本。船坚炮利,不过是身外之物,就像这个世界,实实在在的东西都不是本质的,唯有知晓真正的因果,才能来一场华丽的逆袭。

对于算法之道,也不过以下三点:

  • 精简:不做无用之事
  • 取巧:如有捷径,则必取之
  • 严密:严密入水,方能一丝不漏

而这也只是一个完美算法的必要条件。

我妄想成就一个完美的算法,汇集天下英才的灵智,直到它拥有比肩自然的能力。但是这并不是我选择软件的理由,因为自始至终都是软件选择的我。但是自知此始,一生无悔。

所以,如果你还没想好将要学什么,那就来学编程吧,因为程序可以干任何事。如果你想学很多东西难以抉择,那也来学编程吧,因为只有程序可以做到一切!

观《未来简史》,赏《人月神话》,论《编程珠玑》……算法是大自然最美的艺术品,生于世,便领略到了它的美。现在,轮到我们来创造了!

南开大学软件学院欢迎你!

分类
其它

【考后安利】南开大学了解一下~

对于高三的学弟学妹来说,你们人生中最重要的考验之一刚刚过去了(๑•̀ㅂ•́)و✧

现在的你们,是不是心情异常激动呢ヽ(✿゚▽゚)ノ是不是终于可以开始一个浪浪浪浪浪浪的超级开心自由奔放无拘无束的暑假了呢(。・∀・)ノ゙

当然,考完报志愿也是个很重要的事情呢╮(╯▽╰)╭所以……吃我一波安利吧!南开大学了解一下~

顺便说一句,对于经济上有困难的同学,南开大学的助学政策可是很给力的哦<( ̄︶ ̄)>戳这里看南开大学和国家奖助学金如何帮家庭困难学生圆大学梦~

分类
随笔

从杜甫到闻一多

从杜甫到闻一多

关于爱国诗的浅见及唐代与近现代爱国诗的对比

爱国是一种在不同国家和民族的不同历史时期都广泛存在于社会个体的情感和价值取向,而诗歌恰恰是是一种极适合用于表达情感的文学体裁,因而爱国诗在诗歌中一直是一个庞大的门类。

然而,虽然爱国诗所传达的情感大体上我们可以统一归为“爱国”,但是根据诗人的个人经历和所处的社会环境不同,爱国诗在不同语言和不同时代的表现形式以及具体的情感内涵都有略微不同。

本文将简述我对于爱国诗的认识和理解,并着重以闻一多和杜甫两位诗人为例,从对比中浅析中晚唐和清末民初这两个时代爱国诗的相似之处以及区别。或许不甚专业,但笔者将尽力将自己的见解表达清楚。

首先我们应界定什么是爱国诗。按照当代的观点,爱国应当是对祖国的忠诚和热爱以及对国家发展和利益的维护[1]

受封建王朝“家天下”思想的局限,在古代“爱国”很多时候会和“忠君”画上等号。当然,因为统治者的利益诉求往往就是政权的利益诉求,而政权的利益诉求和国家的利益诉求有其一致性,所以这种思想也是可以理解和认可的。比如我们解读辛弃疾“了却君王天下事,赢得生前身后名”一句时,重心不应在“君王”或“生前身后名”,而应该落在“天下事”。又如李贺的“报君黄金台上意,提携玉龙为君死”,写出了战士的骁勇忠诚和悲壮慷慨,虽忠的是“君”,但卫的是“国”。

此外,很多爱国诗还具有政治批判的属性。在爱国诗的发轫《离骚》中,屈原用“余虽好修姱以鞿羁兮,謇朝谇而夕替”让人从诗人自身的遭遇窥见了时代和政治的黑暗;到了晚唐大厦将倾之时,杜牧的“商女不知亡国恨,隔江犹唱后庭花”借古讽今,痛斥统治阶级的昏庸无能和纵情声色;而在民国时期的现代诗中这样的例子更是数不胜数,如穆旦的《控诉》中“无数的暗杀,无数的诞生”铿锵而毫不避讳地控诉了反动统治阶级的残酷血腥。

爱国情感最深邃的部分——也是很多爱国诗中最重要的部分——是对黎民百姓的同情怜悯和对天下苍生的关怀。李绅最为脍炙人口的“谁知盘中餐,粒粒皆辛苦”把目光转向了处于底层的劳动人民;艾青在《雪落在中国的土地上》一诗中对“生活在草原上的人们的岁月的艰辛”表达了深深的怜悯;牛汉在《血汗马》中用极为明显的暗喻写出了“汗水流尽了”甚至“胆汁流尽了”还要辛劳直到“扑倒在生命的顶点”的贫苦劳工的悲惨。这种爱国,是真正扎跟进大地的爱国,是属于人民的爱国。

综上所述,我认为爱国诗应当有三层立意——其一是对国家和民族的忠诚,也就是诗人的情感抒发和政治愿望;其二是针砭时弊,能够对国家和社会的弊病提出抨击;其三在于能真正认清一国之本在于民,胸怀天下,心系黎民。

 

对“爱国诗”这一概念有了大致的界定,我们再接着从近现代和中晚唐这两个历史时期的相似性说起。

最为显著且几乎无可争议的一点是,这两个时代都有极为严重的军阀割据现象。唐中晚期藩镇割据极为严重,唐王朝几乎处于四分五裂的状态[2];清末民初社会状况也相似,虽然清政府和后来的民国具有名义上的统治权,但实际上的地方政权掌握在各大军阀手中[3]。在政局混乱的社会条件下,民众饱受涂炭之苦;频频燃起的战火使得国家经济条件和物质水平持续低迷甚至下降,百姓的生活条件极为艰苦。这种状况虽为国之不幸,但也让有良知的知识分子更多地关注时局、关注底层人民,客观上为爱国诗提供了创作土壤。

 

因此,中晚唐的爱国诗和近代爱国诗的重要共同点之一在于,大量作品以抨击割据势力的倾轧权斗和为官者的无为作为主题,这也正是上文所提到的爱国诗的政治批判性的一个重要体现。杜甫的《岁暮》是一个很好的例证——

岁暮远为客,边隅还用兵。
烟尘犯雪岭,鼓角动江城。
天地日流血,朝廷谁请缨。
济时敢爱死,寂寞壮心惊。

诗人已是老朽之身,但在国家遭受战乱时仍对于报国有着一腔热血;而“朝廷谁请缨”则颇为讽刺地写出了达官贵人居庙堂之高却于国难之际畏缩不前的懦弱无能。两相对比之下,晚唐政治的黑暗和官场的丑陋极为彻底地暴露了出来。这种对比对于贪官酷吏的自私无能或是愚蠢残暴具有极强的批判效果,在杜甫的爱国诗中很常见,名篇《江南逢李龟年》中运用得更为巧妙——

岐王宅里寻常见,崔九堂前几度闻。
正是江南好风景,落花时节又逢君。

全诗无一字直接提及王公贵族的奢靡,却借一个卖艺乐师生活境遇的落差把这些位高权重者狠狠羞辱了一番,同时又含有对盛世不复的惋惜——而这盛世的衰落还是要归咎于这些人的穷奢极欲和庸碌无为。短短四句,语言平和甚至轻松,却是辛辣至极。

杜甫善从所见所闻中抒情,用对比表意;而作为现代诗早期的代表人物,闻一多的爱国诗在文学上与杜甫有极大的不同。比如《火柴》一诗,很好地运用了现代诗朦胧的特点,有强烈的象征意义——

这些都是君王底
樱桃艳嘴的小歌童:
有的唱出一颗灿烂的明星,
唱不出的,都拆成两片枯骨。

社会底层人民和统治阶级的形象在诗中刻画得非常精妙,统治者草菅人命、鱼肉百姓的罪恶用诗意的语言表述出来的时候,能够给读者强烈的心理冲击力,让人毛骨悚然。

这种隐喻和象征的手法,闻一多在许多作品中运用得都很成熟,如他的代表作之一《死水》——

这是一沟绝望的死水,
清风吹不起半点漪沦。
不如多扔些破铜烂铁,
爽性泼你的剩菜残羹。

也许铜的要绿成翡翠,
铁罐上绣出几瓣桃花;
在让油腻织一层罗绮,
霉菌给他蒸出些云霞。

让死水酵成一沟绿酒,
漂满了珍珠似的白沫;
小珠们笑声变成大珠,
又被偷酒的花蚊咬破。

那么一沟绝望的死水,
也就夸得上几分鲜明。
如果青蛙耐不住寂寞,
又算死水叫出了歌声。

这是一沟绝望的死水,
这里断不是美的所在,
不如让给丑恶来开垦,
看他造出个什么世界。

中华大地的千疮百孔被形象化了,统治阶级的肮脏行径和丑恶嘴脸被具体化了。这首诗的艺术力量,丝毫不亚于在那些军阀和掌权者脸上扇了一个重重的耳光。

正所谓“爱之深,责之切”,闻一多统治者的嘲讽和对祖国伤疤的揭露,正缘于他对祖国深入骨髓的爱。他所期待的,是一个富庶、和谐、美丽、安宁的中国,这与他看到的中国不一样。所以他以笔为武器,用诗去挑战这一切,想要改变这一切。这是爱国诗人质朴纯洁的理想,也是爱国诗最重要的价值体现之一。

 

中晚唐和近代爱国诗另一个重要的共同点,在于有大量作品体现出了民本思想。不管是古代还是近现代以及当代,国家的主体都应当是广大人民。所以,真正优秀的爱国诗,应该着眼于最普通的百姓。杜甫的《茅屋为秋风所破歌》即很好地体现了这一特点。

八月秋高风怒号,卷我屋上三重茅。茅飞渡江洒江郊,高者挂罥长林梢,下者飘转沉塘坳。

南村群童欺我老无力,忍能对面为盗贼。公然抱茅入竹去,唇焦口燥呼不得,归来倚杖自叹息。

俄顷风定云墨色,秋天漠漠向昏黑。布衾多年冷似铁,娇儿恶卧踏里裂。床头屋漏无干处,雨脚如麻未断绝。自经丧乱少睡眠,长夜沾湿何由彻!

安得广厦千万间,大庇天下寒士俱欢颜,风雨不动安如山。呜呼!何时眼前突兀见此屋,吾庐独破受冻死亦足!

此时的杜甫在经济上极度困顿窘迫[4],自己居住的是逼仄狭小而且漏风滴雨的草庐。但纵使处于这样的境地,他心里想的是“安得广厦千万间,大庇天下寒士俱欢颜,风雨不动安如山”,甚至“吾庐独破受冻死亦足”。此时,“处江湖之远”甚至穷困潦倒自身难保的杜甫,仍然时刻想着兼济全天下的穷苦民众。

我通常认为,诗歌最重要的在于情感;但在杜甫的诗中和杜甫本人身上,我看到了比情感更重要的东西——情怀,济世的情怀。

当然,所谓“济世”绝不是高高在上的施舍。虽然杜甫出生于氏族大家,年少时家境优渥,但他的一生却眼看着唐王朝由盛转衰[5]。从盛世到乱世的落差,杜甫有切身的体会,因而他能够真正理解百姓的想法。更准确地说,他已不再是那个带着名门之后的光环、社会地位崇高的知识分子,他已经是百姓中的一员。作为一个百姓,他知道,每个人都要有饭吃,每个人都要有房住;他知道,当他不再高高在上,当每一个曾经高高在上的人都成为人民的一部分,才是真正的以民为本。

当然,我们再来看与杜甫相隔千年的诗人闻一多。闻一多爱国诗中的民本思想,在继承杜甫等古代诗人民本思想的基础上有所发展。近现代以来的历次社会变革和思想启蒙,使得闻一多的爱国诗中民本思想变得更为成熟。

杜甫的民本思想关键在于让百姓安居乐业,要求君王亲民爱民,他在诗中描绘的是一幅和谐美好的生活画卷,是物质上的丰裕[6];而闻一多的诗中,民本思想还包括了尊严和权利——换句话说,闻一多民本思想的终极理想是一个真正属于人民的国家。这在他的《一句话》中有极佳的表达——

有一句话说出就是祸,
有一句话能点得着火,
别看五千年没有说破,
你猜得透火山的缄默?
说不定是突然着了魔,
突然青天里一个霹雳
爆一声:
“咱们的中国!”

这话叫我今天怎样说?
你不信铁树开花也可,
那么有一句话你听着:
等火山忍不住了缄默;
不要发抖,伸舌头,顿脚,
等到青天里一个霹雳
爆一声:
“咱们的中国!”

这首诗把旧时代人民地位的卑微和受到的压迫摆上了桌面,把统治阶级的血腥蛮横也拉到了台前。诗句语气强烈,一字一句像枪弹一样打在统治者身上。这是对之前五千年等级社会传统和思想的尖锐质问,更是对人民主权的美好向往和民本思想的强烈呼吁。

闻一多知道,民本思想,尤其是人民主权的想法太过大胆;他也知道,在他之前的五千年里,没有人敢说。但他更知道,随着历史车轮的前行,国家终归属于人民;他更知道,民众对封建压迫的愤懑总有一天会彻底爆发。

闻一多深沉地爱着祖国,他坚信人民才是国家的根本,坚信这个国家的主权总有一天会交到人民的手上——纵使他的一生看到的都是灰暗的满目疮痍的中国。

但这就是爱国诗人——看见的是黑暗,但永远相信美好的事情总会发生。

 

参考文献:

[1]罗大文.试析爱国主义的内涵、结构与功能[J].学术论坛,2006(06):58-61+87.
[2]樊文礼.安史之乱以后的藩镇形势和唐代宗朝的藩镇政策[J].烟台师范学院学报(哲学社会科学版),1995(04):40-45.
[3]来新夏,莫建来.50年来北洋军阀史研究述论[J].社会科学战线,1999(05):1-17.
[4]闵和顺.城中十万户 此地三两家——《茅屋为秋风所破歌》新探[J].云梦学刊,2012,33(03):64-68.
[5]胡可先.杜甫与安史之乱[J].杜甫研究学刊,2003(02):1-11.
[6]冯建国.论杜甫的民本思想[J].清华大学学报(哲学社会科学版),2007(02):17-28.

分类
作品

随手拍(五)

考完上机考试好累啊╮(╯▽╰)╭又懒得写稿了(ˉ▽ ̄~)还是扔一张随手拍上来吧~

[mdx_fold title=”拍摄参数”]
ISO:800
快门:1/1250秒
光圈:F/4
焦距:等效焦距33mm
[/mdx_fold]

[mdx_fold title=”随手拍系列”]
第一期
第二期
第三期
第四期
第五期
第六期
第七期
href=”https://blog.potatofield.cn/%e9%9a%8f%e6%89%8b%e6%8b%8d%ef%bc%88%e5%85%ab%ef%bc%89/”>第八期
[/mdx_fold]

分类
程序和算法

大数据和机器学习 基础篇 分类 支持向量机SVM

分类算法是机器学习中的一个重点,也是人们常说的“有监督的学习”。这是一种利用一系列已知类别的样本来对模型进行训练调整分类器的参数,使其达到所要求性能的过程,也成为监督训练或有教师学习。

注:本文中用到的Python及其模块安装教程参见


支持向量机SVM

支持向量机SVM是一种比较抽象的算法概念,全称是Support Vector Machine,它可以用来做模式识别,分类或者回归的机器学习。

前面介绍过机器学习是为了解决样本的具体分类映射的问题,构造一个算法,把已知样本的特征和分类情况做一个逻辑映射关系,这样碰到样本时就能用这个算法把它进行分类了。

例如,大于零的实数叫正数,小于零的实数叫负数:

\(
属性=
\left\{
\begin{array}{c}
Positive(x>0)\\
\\
Negative(x<0) \end{array} \right.
\)

但这个过程其实并不是一个机器学习过程,都是人来告诉计算机判定的定义如何,然后计算机根据这个判定的定义来处理每一个待定的对象。这里面计算机确实没有学习的过程。

下面我们把上面的例子改变一下:


年龄和好坏

假设某公司有任务,给客户分类,看看什么样的用户质量比较高;假设客户信息只有年龄这一项,客户信息表如下:

  • 表1 客户信息表

[mdx_table header=”true”]
客户编号
客户年龄
客户质量
客户编号
客户年龄
客户质量
—–
XXXX
34

XXXX
30

—–
XXXX
33

XXXX
25
不好
—–
XXXX
32

XXXX
23
不好
—–
XXXX
31

XXXX
22
不好
—–
XXXX
30

XXXX
18
不好
[/mdx_table]

  • 客户信息的数轴表示:

从上图可以看出,年龄在30以上的客户质量都好,在25一下的都不好。那么就可以考虑在30和25中间切一刀,一边是好一边是不好。也就是从27.5分开。

  • 以27.5为分界:

例如,来了一个27岁的客户,那么他应该属于客户质量好的客户。但是如果没有办法“一刀切”怎么办?例如客户信息如下表:

  • 表2 客户信息表

[mdx_table header=”true”]
客户编号
客户年龄
客户质量
客户编号
客户年龄
客户质量
—–
XXXX
34

XXXX
30

—–
XXXX
33

XXXX
25
不好
—–
XXXX
32

XXXX
23

—–
XXXX
31
不好
XXXX
22
不好
—–
XXXX
30

XXXX
18
不好
[/mdx_table]

  • 客户信息的数轴表示:

这个比较麻烦,因为没法做到“一刀切”。那么现在有以下两种选择:

  • 把这两类全部标出来。
\(
属性=
\left\{
\begin{array}{c}
Good(x≥30,x≠31)\\
\\
Good(x=23)\\
\\
NotGood(x≤25,x≠23)\\
\\
NotGood(x=31)
\end{array}
\right.
\)

这是一个分段函数,虽然准确但是啰嗦。在实际情况中一般可能会有10000个甚至更多的客户,所以这个函数写起来会相当麻烦。所以这种标记方法很容易产生过拟的问题。

  • 一刀切。反正只要一刀切就会非常简洁,如果一刀切下去虽然两边的类都不纯或者一边的类不纯,但是只要不纯的程度在能容忍的范围内就可以。
\(
属性=
\left\{
\begin{array}{c}
Good(x≥27.5)\\
\\
NotGood(x<27.5)\\ \end{array} \right.
\)

这次还是从27.5切,大于等于27.5就算“好”,小于27.5就算“不好”。那么在这个分类中“好”类的不纯度为\(\frac{1}{6}\);“不好”类的不纯度为\(\frac{1}{4}\)。如果说在不做这种数据分析的情况下,发展10个客户,有6个是“好”客户,4个是“不好”的客户;现在改进后虽然有一定的误判率,约为16.7%——由于分类不纯的问题,但是直接过滤掉了一定的对象,发展10个客户,有8.3个是“好”客户,1.7个是“不好”的客户。从数值上看,方案策略的提升还是有改进的。

总结一下,有以下几个关键点:

  • 关键点1:切下去的点在SVM算法体系里叫“超平面”。这个“超平面”是一个抽象的面概念,在一维空间里就是一个点,用\(x+A=0\)的点来表示;二维空间里就是一条线,用\(Ax+By+C=0\)的直线来表示;三维空间里就是一个面,用\(Ax+By+Cz+D=0\)的平面来表示;四维空间了就用\(Ax+By+Cz+Dα+E=0\)来表示;以此类推……上述4个方程都可以变形为:
    \( x=-A\)
    \( y=-\frac{A}{B}x-\frac{C}{B}\)
    \( z=-\frac{A}{C}x-\frac{B}{C}y-\frac{D}{C}\)
    \( α=-\frac{A}{D}x-\frac{B}{D}y-\frac{C}{D}z-\frac{E}{D}\)
  • 关键点2:过拟问题。一般来说,设计分类器都是要尽量避免过拟的。过拟会给归纳过程带来很大的麻烦,而且在应用的过程中也非常不方便,只要精确度达到标准就足够了。
  • 关键点3:不纯度问题。不纯度和精确度是一对矛盾,精确度越高那么不纯度就越低,反之,不纯度越高精确度就越低。分类器的研究和调整的过程就是一个精度和成本平衡的过程,所以并不是不纯度越低越好,而是在实际生产中操作成本一样的情况下,不纯度越低越好。

定义距离

在一个平面直角坐标系中,有一些样本点作为训练点,一些被标记为类别\(X\),一些被标记为非类别\(X\)。如何想把两种类别的点区分开,最朴素的想法是,如果能找到一条直线\(Ax+By+C=0\)能够恰好把它们区分成两个部分就最好了。

如何求解恰当的\(A,B,C\),恰当的标准是让类别\(X\)中与该直线最近的样本点的距离和非类别\(X\)中的样本点与该直线的距离最大。

在平面直角坐标系中,如果有一条直线\(Ax+By+C=0\),那么点\((x_0,y_0)\)到该直线的距离如下:

\( d=\frac{|Ax_0+By_0+C|}{\sqrt{A^2+B^2}}\)

与数轴上类似:“\(x-27.5>0\)的都是分类为‘好’的样本,\(x-27.5<0\)的则都不是分类为‘好’的样本”。如果以\(Ax+By+C=0\)这样一个方程做分隔,可以发现,所有\(X\)类别中的样本点都满足\(Ax+By+C>0\),而所有非\(X\)类别中的样本点都满足\(Ax+By+C<0\),这就是最开始构造这条直线的目的。 推广到N维空间,这个超平面的公式可以简写为: \( g(v)=wv+b\) 这里w是系数,v是样本向量,b是常数。 而N维空间上的距离为: \( d=\frac{|g(v_0)|}{||w||}\)


升维

如果样本是线性不可分的,那么就需要升维来解决这个问题——这才是SVM算法最为吸引人的部分。

先来看一个一维数据的例子,来说明升维的概念。

假设在数轴上给出一些数据,其中[-2,2]区间内的被标记为分类1,其余的都是分类0,我们可以把分段函数写成如下的形式:

\(
f(x)=
\left\{
\begin{array}{c}
1(-x^2+4>0)\\
\\
0(-x^2+4≤0)
\end{array}
\right.
\)

或者可以认为:

\(
f(y)=
\left\{
\begin{array}{c}
1(y>0)\\
\\
0(y≤0)
\end{array}
\right.\)

\(\left\{
\begin{array}{c}
y=-x^2+4\\
\\
z=f(y)
\end{array}
\right.
\)

这实际上就是\(y=-x^2+4\)这个函数在\(y=0(x轴)\)这条直线上的投影把样本点分开。

  • \(y=-x^2+4\)的图形:

在二维空间中也有类似的方式。

例如,样本向量v距离原点(0,0)的距离为1以内分类被标记为0,其余都为1。同样是线性不可分的,但是可以构造一个这样的函数:

\(
f(x,y)=
\left\{
\begin{array}{c}
1(x^2+y^2≥1)\\
\\
0(x^2+y^2<1) \end{array} \right.
\)

或者可以这么认为:

\(
f(z)=
\left\{
\begin{array}{c}
1(z≥1)\\
\\
0(z<1) \end{array} \right.
\)

\(
\left\{
\begin{array}{c}
z=x^2+y^2\\
\\
α=f(z)
\end{array}
\right.
\)

可以看到,在一维空间上解决线性不可分问题是把函数映射到二维空间,使得一维空间上的分类边界是二维空间上的分类函数在以为空间上的投影;而在二维空间上解决线性不可分问题是把函数映射到三维空间,使得二维空间上的分类边界是三维空间上的分类函数在二维空间上的投影。那么所有的n维空间上的线性不可分问题都可以考虑映射到n+1维上去构造分类函数,使得它在n维空间上的投影能够将两个类别分开。

这个构造的过程在SVM里是使用核函数(Kernel)来进行的。核函数的目的很单纯,即只要在当前维度的样本是线性不可分的,就一律映射到更高的维度上去,在更高的维度上找到超平面,得到超平面方程。函数本身表示的只是一个变量代换关系。


示例

下面还是用客户信息的列表给出示例。

  • 表3 客户信息表

[mdx_table header=”true”]
客户编号
客户年龄
客户质量
客户编号
客户年龄
客户质量
—–
XXXX
34

XXXX
30

—–
XXXX
33
不好
XXXX
25
不好
—–
XXXX
32

XXXX
23

—–
XXXX
31
不好
XXXX
22
不好
—–
XXXX
30

XXXX
18
不好
[/mdx_table]

在这个例子中,客户年龄和客户质量之间明显是没办法做线性分隔了。这是可以用SVM来做分类。
在Python的Scikit-learn库中,用到的是一个叫做SVC的类,SVC是Support Vector Classification的缩写,即支持分类向量机。SVC支持的核函数包括linear(线性核函数),poly(多项式核函数),rbf(径向核函数),sigmoid(神经元激活核函数),precomputed(自定义核函数),默认使用rbf(径向核函数)。

示例代码如下:

#支持向量机SVM
from sklearn import svm

#年龄
X=[[34],[33],[32],[31],[30],[30],[25],[23],[22],[18]]
#质量
y=[1,0,1,0,1,1,0,1,0,1]

#把训练数据和对应的分类放入分类器中进行训练
#这里使用rbf(linear,poly,rbf,sigmoid,precomputed)
clf=svm.SVC(kernel='rbf').fit(X,y)

#预测年龄29的客户的质量
p=[[29]]
print(clf.predict(p))

预测年龄为29的客户的质量,输出为1,表示客户质量为“好”。

在这个例子中,使用的是rbf核函数,这也是最适合做非线性关系分类标准的首选核函数。如果这几种核函数实在不知道该用哪个,那就在实际场景中多做对比测试,看看哪一种的正确率最高即可。


#小结

SVM解决问题的方法有以下几步:

  1. 把所有的样本和其对应的分类标记交给算法进行训练。
  2. 如果发现线性可分,那就直接找出超平面。
  3. 如果发现线性不可分,那就映射到n+1维空间,找出超平面。
  4. 最后得到超平面的表达式,也就是分类函数。

想了解更多关于大数据和机器学习:

分类

北方的夏夜

夏天,天越来越热了。热得和南方一样。想家了。

北方的夏夜
和南方的一样
有躲不开的蚊虫
和逃不掉的热浪

北方的夏夜
和南方的一样
草丛里螽丝啼鸣
天空中月光流淌

北方的夏夜
和南方的一样
雨水有泥土味道
空气带花的芬芳

北方的夏夜
和那南方小城不一样
我在北方只是过客
那南方小城才是我的家乡

2018年六月作于天津

分类
其它

【考前安利】助梦南开

去年参加第四届“助学、筑梦、铸人”主题宣传比赛的参赛作品,后来又被学校学工部拿去作为助学政策宣传材料(*/ω\*)这两天忽然想起来还没发表过,就扔到哔哩哔哩了(๑•̀ㅂ•́)و✧

正好现在高三的学弟学妹们也马上要高考了吧(~ ̄▽ ̄)~趁着考前先来一波安利,南开大学了解一下~

话说,在WordPress里面嵌入哔哩哔哩的视频其实还是稍微有点麻烦的╮(╯▽╰)╭下次写篇文章讲讲怎么用HTML的iframe和CSS实现这个吧(啊哈哈哈哈哈哈哈哈又可以水一篇文章了)o(*≧▽≦)ツ

分类
程序和算法

大数据和机器学习 基础篇 分类 隐马尔可夫模型

分类算法是机器学习中的一个重点,也是人们常说的“有监督的学习”。这是一种利用一系列已知类别的样本来对模型进行训练调整分类器的参数,使其达到所要求性能的过程,也成为监督训练或有教师学习。

注:本文中用到的Python及其模块安装教程参见


隐马尔可夫模型

隐马尔可夫模型(Hidden Markov Model,HMM)最初由L. E. Baum发表在20世纪70年代一系列的统计学论文中,随后在语言识别,自然语言处理以及生物信息等领域体现了很大的价值。

首先提出马尔可夫链的概念:

  • 在观察一个系统变化的时候,它下一个状态(第n+1个状态)如何的概率只需要观察和统计当前状态(第n个状态)即可正确得出。

如果在一个完整的观察过程中有一些状态的转换,如下图中的\(y_1\)到\(y_n\)。在观察中\(y_1\)到\(y_n\)的状态存在一个客观的转化规律,但是没办法直接观测到,观测到的是每个\(y\)状态下的输出\(x\),即\(x_1\)到\(x_n\)。需要通过\(x_1\)到\(x_n\)这些输出值来进行模型建立和计算状态转移概率。

  • 隐马尔可夫链示意图:

为了比较容易理解整个过程,下面举一个例子,假设有3中不同的骰子:
第一个骰子是常见的骰子(称这个骰子为D6),6个面,每个面(1,2,3,4,5,6)出现的概率是\(\frac{1}{6}\)。
第二个骰子是一个四面体(称这个骰子为D4),每个面(1,2,3,4)出现的概率是\(\frac{1}{4}\)。
第三个骰子有8个面(称这个骰子为D8),每个面(1,2,3,4,5,6,7,8)出现的概率是\(\frac{1}{8}\)。

  • 三种骰子和掷骰子可能产生的结果:

先随机选择一个骰子,然后再用它掷出一个数字,并记录下这个选择和数字。先从三个骰子里挑一个,挑到每个骰子的概率都是\(\frac{1}{3}\)。然后掷骰子,得到一个数字,1,2,3,4,5,6,7,8中的一个。不停地重复上述过程,会得到一串数字,每个数字都是1,2,3,4,5,6,7,8中的一个。

例如,投掷骰子10次,可能得到这么一串数字:1,6,3,5,2,7,3,5,2,4,这串数字叫做可见状态链,也就是记录的这组数字,可是前面介绍的\(x_n\)。但是在隐马尔可夫模型中,不仅有可见状态链,还有一串隐含状态链。在这个例子里,这串隐含状态链就是选出的骰子的序列。例如本次中,隐含状态链是:D6,D8,D8,D6,D4,D8,D6,D6,D4,D8,如下图所示:

  • 隐马尔可夫模型示意图:

一般来说,HMM中的马尔科夫链就是指隐含状态链,因为实际是隐含状态之间存在转换概率(Transition Probability)

其实对于HMM来说,如果提前知道所有隐含状态之间的转换概率和所有隐含状态到所有可见状态之间的输出概率,进行模拟是相当容易的。但应用HMM模型时,往往缺失一部分信息,有时候知道骰子有几种,每种骰子是什么,但是不知道掷出来的骰子序列;有时候只是看到了很多次掷骰子的结果,剩下的什么都不知道。如何应用算法去估计这些缺失的信息,就成了一个很重要的问题。

HMM模型相关的算法主要分为3类,分别解决3中问题:

  • 问题1:知道骰子有几种(隐含状态数量),每种骰子是什么(转换概率),根据掷骰子掷出的结果(可见状态链),想知道每次掷出来的都是哪种骰子(隐含状态链)。

这个问题在语音识别领域叫做解码问题。这个问题其实有两种解法,会给出两个不同的答案。每个答案都正确,只是这些答案的意义不一样。第一种解法求最大似然状态路径,通俗地说,就是求一串骰子序列,这串骰子序列产生观测结果的概率最大。第二种解法不是求一组骰子序列,而是求每次掷出的骰子分别是某种骰子的概率。

例如,看到结果后,可以求得第一次掷骰子是D4的概率是0.5,D6的概率是0.3,D8的概率是0.2。

  • 问题2:知道骰子有几种(隐含状态数量),每种骰子是什么(转换概率),根据掷骰子掷出的结果(可见状态链),想知道掷出这个结果的概率。

这个问题看似意义不大,因为掷出来的结果很多时候都对应了一个比较大的概率。这个问题的目的其实是检测观察到的结果和已知的模型是否吻合。如果很多次结果都对应了比较小的概率,那么说明已知的模型很有可能是错的,有人偷偷把骰子换了。

  • 问题3:知道骰子有几种(隐含状态数量),不知道每种骰子是什么(转换概率),观测到很多次掷骰子的结果(可见状态链),想反推出每种骰子是什么(转换概率)。这个问题很重要,因为这是最常见的情况。很多时候只有可见结果,不知道HMM模型中的参数,需要从可见结果估计出这些参数,这是建模的一个必要步骤。

维特比算法

维特比算法用于解决前面提到的问题1的解最大似然路径的问题。

还是考虑上面的例子:

首先掷一次骰子,结果为1,此时对应的最大概率骰子序列就是D4,因为D4产生1的概率是\(\frac{1}{4}\),高于\(\frac{1}{6}\)和\(\frac{1}{8}\)。

把这个情况扩展,掷两次骰子,结果分别为1,6。这是问题变得复杂起来,要计算3个值,分别是第二个骰子是D4,D6,D8的最大概率。显然,要取得最大概率,第一个骰子必须为D4,此时,第二个骰子取到D6的最大概率如下:

\(P2(D6)\) \(=P(D4)*(1|D4)*(D6|D4)*P(6|D6)\) \(=\frac{1}{3}*\frac{1}{4}*\frac{1}{3}*\frac{1}{6}=\frac{1}{216}\)

同样的,可以计算第二个骰子是D4或D8时的最大概率。发现,第二个骰子取到D6的概率最大。而使这个概率最大时,第一个骰子为D4。所以最大概率骰子序列就是D4,D6。

继续扩展,掷三次骰子,计算第三个骰子分别是D4,D6,D8的最大概率。再次发现,要取到最大概率,第二个骰子必须为D6。这时,第三个骰子取到D4的最大概率如下:

\(P3(D4)\) \(=P2(D6)*P(D4|D6)*P(3|D4)\) \(=\frac{1}{216}*\frac{1}{3}*\frac{1}{4}=\frac{1}{2592}\)

和计算两个骰子序列概率的方法一样,还可以计算第三个骰子是D6或D8时的最大概率。可以发现,第三个骰子取D4的概率最大。而使这个概率最大时,第二个骰子为D6,第一个骰子为D4。

既然掷骰子1到3次可以算,掷多少次都可以,以此类推。

  • 可以发现,要求最大概率骰子序列时要做下面几件事情:

首先,不管序列多长,要从序列长度为1算起,算序列的长度为1时取到每个骰子的最大概率。然后,逐渐增加长度,每增加一次长度,重新算一遍在这个长度下最后一个位置取到每个骰子的最大概率。因为上一个长度下取到每个骰子的最大概率都算过了,重新计算其实不难。当算到最后一位时,就知道最后一位哪个骰子的概率最大了。然后,要把对应这个最大概率的序列从后往前推出来。这就是在刚刚掷骰子的例子中展示出的完整维比特算法。


维特比算法应用——打字提示功能

维比特算法研究的是一种链的可能性问题。现在应用最广的领域是打字提示功能。

Windows中能够使用的输入法有很多种,在这里以全拼为例。在使用输入法时,输入的是英文字母,在这个应用中,隐藏的序列时真实要输入的中文字符和词汇,显示的部分是输入的英文字符。

在输入jin时,输入法软件会猜测想要输入“近”,“斤”,“今”等。而这种排序不是瞎猜的,通常是根据统计数据而来。也就是说“近”,“斤”,“今”这样的顺序一般是根据使用人的输入习惯形成的——在平时打字聊天的过程中“近”出现在词汇或句子输入开始的概率大于“斤”,而“斤”大于“今。

但是输入了tian时就不一样了。“今天”作为一个词汇,比其他任何一个被拼作jintian的词汇都使用得更为高频。也可以理解为,当输入tian时,由jin(今)到tian(天)这条路径的概率是最高的,这是把“今天”这个词汇放在第一个的原因。

后面输入了几个其他的完整词汇:“我们”,“应该”,“做些”,“什么”,输入法也会继续对这些词汇在句子中的路径概率进行计算,每次输入都会猜测一次到目前的输入状态为止最有可能的那条路径,那么看到的这个第一顺位的词汇,准确说是一个句子——“今天我们应该做些什么”就是猜测到的最优的结果,它比任何一种路径产生的概率都要高。

在具体操作的时候,这个马尔可夫模型的训练(拼音串的输入与最终产生汉字串的输出)应该来源于本地的输入者的习惯和互联网的结合。

下面模拟输入法的猜测方法给出一个算法示例,先给出各级转换矩阵,如下表:

  • 表1 jin概率矩阵:

[mdx_table header=”true”]
jin
概率
—–

0.3
—–

0.2
—–

0.1
—–

0.06
—–

0.03
[/mdx_table]

  • 表2 jin-tian转移矩阵:

[mdx_table header=”true”]
jin-tian





—–

0.001
0.001
0.001
0.001
0.001
—–

0.001
0.001
0.001
0.001
0.001
—–

0.990
0.001
0.001
0.001
0.001
—–

0.001
0.001
0.850
0.001
0.001
—–

0.001
0.001
0.001
0.001
0.001
[/mdx_table]

  • 表3 wo概率矩阵:

[mdx_table header=”true”]
wo
概率
—–

0.400
—–

0.150
—–

0.090
—–

0.050
—–

0.030
[/mdx_table]

  • 表4 wo-men转移矩阵:

[mdx_table header=”true”]
wo—men





—–

0.970
0.001
0.003
0.001
0.001
—–

0.001
0.001
0.001
0.001
0.001
—–

0.001
0.001
0.001
0.001
0.001
—–

0.001
0.001
0.001
0.001
0.001
—–

0.001
0.001
0.001
0.001
0.001
[/mdx_table]

在输入一个完整拼音后,用户就会按回车或数字把输入备选框中的汉字输出,当单字词输出时就会由统计产生“jin概率矩阵”和“wo概率矩阵”这样的统计结果,而当用户输入的是一个双字词是就会产生“jin-tian转移矩阵”和“wo-men转移矩阵”这样的统计结果。而且每个双字词,三字词等的输入统计都用这种方法。在输入双字词汉字拼音时会根据转移概率表进行计算。多个词相连就是多个转移矩阵的概率相乘计算,从而得到概率最大的输入可能项。

示例代码如下:

#维特比算法模拟输入法
import numpy as np

jin=['近','斤','今','金','尽']
jin_per=[0.3,0.2,0.1,0.06,0.03]

jintian=['天','填','田','甜','添']
jintian_per=[
[0.001,0.001,0.001,0.001,0.001],
[0.001,0.001,0.001,0.001,0.001],
[0.099,0.001,0.001,0.001,0.001],
[0.002,0.001,0.085,0.001,0.001],
[0.001,0.001,0.001,0.001,0.001]
]

wo=['我','窝','喔','握','卧']
wo_per=[0.400,0.150,0.090,0.050,0.030]

women=['们','门','闷','焖','扪']
women_per=[
[0.970,0.001,0.003,0.001,0.001],
[0.001,0.001,0.001,0.001,0.001],
[0.001,0.001,0.001,0.001,0.001],
[0.001,0.001,0.001,0.001,0.001],
[0.001,0.001,0.001,0.001,0.001]
]

N=5

def found_from_oneword(oneword_per):
    index=[]
    values=[]
    a=np.array(oneword_per)
    for v in np.argsort(a)[::-1][:N]:
        index.append(v)
        values.append(oneword_per[v])
    return index,values

def found_from_twoword(oneword_per,twoword_per):
    last=0
    for i in range(len(oneword_per)):
        current=np.multiply(oneword_per[i],twoword_per[i])
        if i==0:
            last=current
        else:
            last=np.concatenate((last,current),axis=0)
    index=[]
    values=[]
    for v in np.argsort(last)[::-1][:N]:
        index.append([v//5,v%5])
        values.append(last[v])
    return index,values

def perdict(word):
    if word=='jin':
        for i in found_from_oneword(jin_per)[0]:
            print(jin[i])
    elif word=='jintian':
        for i in found_from_twoword(jin_per,jintian_per)[0]:
            print(jin[i[0]]+jintian[i[1]])
    elif word=='wo':
        for i in found_from_oneword(wo_per)[0]:
            print(wo[i])
    elif word=='women':
        for i in found_from_twoword(wo_per,women_per)[0]:
            print(wo[i[0]]+women[i[1]])
    elif word=='jintianwo':
        index1,values1=found_from_oneword(wo_per)
        index2,values2=found_from_twoword(jin_per,jintian_per)
        last=np.multiply(values1,values2)
        for i in np.argsort(last)[::-1][:N]:
            print(jin[index2[i][0]]+jintian[index2[i][1]]+wo[i])
    elif word=='jintianwomen':
        index1,values1=found_from_twoword(jin_per,jintian_per)
        index2,values2=found_from_twoword(wo_per,women_per)
        last=np.multiply(values1,values2)
        for i in np.argsort(last)[::-1][:N]:
            print(jin[index1[i][0]]+jintian[index1[i][1]]+wo[index2[i][0]]+women[index2[i][1]])
    else:
        pass

if __name__=='__main__':
    print('jin:')
    perdict('jin')
    print('jintian:')
    perdict('jintian')
    print('wo:')
    perdict('wo')
    print('women:')
    perdict('women')
    print('jintianwo:')
    perdict('jintianwo')
    print('jintianwomen:')
    perdict('jintianwomen')

根据算法,示例输入“jin”,“jintian”,“wo”,“women”,“jintianwo”和“jintianwomen”时的排序为:
[mdx_fold title=”输出结果:”]
jin:





jintian:
今天
金田
近天
近填
近田
wo:





women:
我们
我闷
我门
我焖
我扪
jintianwo:
今天我
金田窝
近天喔
近填握
近田卧
jintianwomen:
今天我们
金田我闷
近田我扪
近填我焖
近天我门
[/mdx_fold]

在实际应用过程中,这个概率矩阵会是一个系数矩阵。而且转移概率足够小,如小于0.001时可以认为是输入统计中的噪声点,不进行词汇输入推荐,这样输入备选框的前面也只会出现高频输入词汇,这样备选框比较简洁。


想了解更多关于大数据和机器学习:

分类
其它

今天过节不写稿

今天马铃薯田地不打算写新东西,因为超级无敌善良可爱的土豆哥今天要过儿童节(๑•̀ㅂ•́)و✧

分类

码农

作为一只未来码农兼诗歌爱好者,今天来点不一样的——一首给所有码农的诗(๑•̀ㅂ•́)و✧各位同仁,共勉!

有人羡慕我们工作的光鲜
帮助各行各业走向前沿
着力营造舒心的体验
加班颠倒了黑夜和白天

有人惊叹我们成就的亮眼
领着时代提前看见明天
用户享受智能和方便
牺牲程序员的日日年年

化不到最优的算法
学不完特性的语言
一再缩短的睡眠时间
不停后退的发际线

道是农家少清闲
码农的辛酸又有谁能看见
都知道盘中餐的粒粒辛苦
却不珍惜程序的饕餮盛宴

承受艰辛和误解
但绝不放下偏执的信念
用代码的语句和标签
改变世界的方方面面

2018年五月作于天津