Kaka Chen 2017-11-17T13:25:09+00:00 silver.accc@gmail.com Iris species python数据可视化 2017-11-17T00:00:00+00:00 Kaka Chen http://kakack.github.io/2017/11/Iris Species Python数据可视化 Iris Speciessklearn里很常用而简单的一个分类数据集,数据除了id和label外,还有四个属性:

  • sepal length (cm)
  • sepal width (cm)
  • petal length (cm)
  • petal width (cm)

为了能在二维图像中展示,以下就暂时只有petal length和petal width两个属性进行展示。

import pandas as pd
import numpy as np
from sklearn import datasets

iris = datasets.load_iris()
# 从sklearn.datasets导入数据,格式是sklearn.unit形式的

X = iris.data[:, [2, 3]]
y = iris.target
iris_df = pd.DataFrame(iris.data[:, [2, 3]], 
	columns=iris.feature_names[2:])
	
# 读取iris的data和target存入X和y,存入pandas.DataFrame里
# 暂时只保留petal length和petal width两个属性

print(iris_df.head())
print('\n' + 'The unique labels in this data are ' + 
	str(np.unique(y)))
# 打印前5个数据查看内容

输出结果:

   petal length (cm)  petal width (cm)
0                1.4               0.2
1                1.4               0.2
2                1.3               0.2
3                1.5               0.2
4                1.4               0.2

The unique labels in this data are [0 1 2]

将完整的数据集以7:3的比例分割成训练集和测试集

X_train, X_test, y_train, y_test = 
	train_test_split(X, y, test_size=.3, random_state=0)

print('There are {} samples in the training set 
	and {} samples in the test set'.
	format(X_train.shape[0], X_test.shape[0]))

输出结果:

There are 105 samples in the training set 
	and 45 samples in the test set

在常见的数据预处理中,将数据标准化是很重要的,能把被处理的数据缩放到一个合适的范围,有助于之后算法的收敛和准确性。

from sklearn.preprocessing import StandardScaler

sc = StandardScaler()
sc.fit(X_train)
# 用StandardScaler来fit训练数据

X_train_std = sc.transform(X_train)
X_test_std = sc.transform(X_test)

print('After standardizing our features, 
	the first 5 rows of our data now look like this:\n')
print(pd.DataFrame(X_train_std, 
	columns=iris_df.columns).head())
	
# 查看标准化结果

输出结果

After standardizing our features, 
	the first 5 rows of our data now look like this:

   petal length (cm)  petal width (cm)
0          -0.182950         -0.291459
1           0.930661          0.737219
2           1.042022          1.637313
3           0.652258          0.351465
4           1.097702          0.737219

然后通过matplotlib.pyplot包来生成原始的分类图像

from matplotlib.colors import ListedColormap
import matplotlib.pyplot as plt

markers = ('s', 'x', 'o')
# 设定标记类型
colors = ('red', 'blue', 'lightgreen')
# 设定颜色
cmap = ListedColormap(colors[:len(np.unique(y_test))])
for idx, cl in enumerate(np.unique(y)):
	plt.scatter(x=X[y == cl, 0], y=X[y == cl, 1], c=cmap(idx), 
		marker=markers[idx], label=cl)
plt.show()

在正式应用模型之前,先定义一个之后展示的方法。

def versiontuple(v):
    return tuple(map(int, (v.split("."))))


def plot_decision_regions(X, y, classifier, 
	test_idx=None, resolution=0.02):

    # setup marker generator and color map
    markers = ('s', 'x', 'o', '^', 'v')
    colors = ('red', 'blue', 'lightgreen', 'gray', 'cyan')
    cmap = ListedColormap(colors[:len(np.unique(y))])

    # plot the decision surface
    x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max() + 1
    x2_min, x2_max = X[:, 1].min() - 1, X[:, 1].max() + 1
    xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, resolution),
                           np.arange(x2_min, x2_max, resolution))
    Z = classifier.predict(np.array([xx1.ravel(), xx2.ravel()]).T)
    Z = Z.reshape(xx1.shape)
    plt.contourf(xx1, xx2, Z, alpha=0.4, cmap=cmap)
    plt.xlim(xx1.min(), xx1.max())
    plt.ylim(xx2.min(), xx2.max())

    for idx, cl in enumerate(np.unique(y)):
        plt.scatter(x=X[y == cl, 0], y=X[y == cl, 1],
                    alpha=0.8, c=cmap(idx),
                    marker=markers[idx], label=cl)
    plt.show()

使用SVM模型

svm = SVC(kernel='rbf', random_state=0, gamma=.10, C=1.0)
# 用rbf核
svm.fit(X_train_std, y_train)

print('The accuracy of the svm classifier on training data is 
	{:.2f} out of 1'.format(svm.score(X_train_std, y_train)))
print('The accuracy of the svm classifier on test data is 
	{:.2f} out of 1'.format(svm.score(X_test_std, y_test)))
# 打印svm模型在训练集和测试集上的得分

plot_decision_regions(X_test_std, y_test, svm)
# 展示图像

输出结果:

The accuracy of the svm classifier 
	on training data is 0.95 out of 1
The accuracy of the svm classifier 
	on test data is 0.98 out of 1

使用KNN模型

knn = KNeighborsClassifier(n_neighbors=5, p=2, metric='minkowski')
knn.fit(X_train_std, y_train)

print('The accuracy of the knn classifier is 
	{:.2f} out of 1 on training data'.
	format(knn.score(X_train_std, y_train)))
print('The accuracy of the knn classifier is 
	{:.2f} out of 1 on test data'.
	format(knn.score(X_test_std, y_test)))

plot_decision_regions(X_test_std, y_test, knn)

输出结果:

The accuracy of the knn classifier 
	is 0.95 out of 1 on training data
The accuracy of the knn classifier 
	is 1.00 out of 1 on test data

使用XGB

xgb_clf = xgb.XGBClassifier()
xgb_clf = xgb_clf.fit(X_train_std, y_train)

print('The accuracy of the xgb classifier is 
	{:.2f} out of 1 on training data'.
	format(xgb_clf.score(X_train_std, y_train)))
print('The accuracy of the xgb classifier is 
	{:.2f} out of 1 on test data'.
	format(xgb_clf.score(X_test_std, y_test)))

plot_decision_regions(X_test_std, y_test, xgb_clf)

输出结果:

The accuracy of the xgb classifier 
	is 0.98 out of 1 on training data
The accuracy of the xgb classifier 
	is 0.98 out of 1 on test data

在测试集上,KNN表现最好,而在训练集上,SVM和XGB表现更加优越。

]]>
历险大西北 2017-10-10T00:00:00+00:00 Kaka Chen http://kakack.github.io/2017/10/历险大西北 从西北回来已经两天了,趁着这几天上班没什么事情,就把这趟旅行的游记写了。原则上我们是去敦煌研究院帮忙验收,但实际上还是旅游为主。很久没经历那么赶路的旅行了,我看了一眼地图,将近2500km的环线行程,我们就这么开了下来,也是佩服自己,可能以后再也不会这么玩命开车了。


从西宁到青海湖

因为机票贵的缘故,选择了杭州往返兰州,然后落地兰州之后马上租了车前往西宁过夜。因为晚上到白天一早就走的缘故,我并没有在西宁多逗留很久。西宁给我的印象就是一个很普通的标准的中国城市,区别仅在于这边确实有浓郁的回族风味,比如满大街的羊肉烧烤和牛肉拉面店。第一天我确实很新奇,对他们这的饮食充满了好奇,尤其是发现他们居然馆子里面没有勺子或者调羹,喝羊杂汤都是端起大碗嘴对嘴长流水。西北民风之彪悍也可见一斑,跟我们江南一口三抿的喝汤风格截然不同。

第一站是西宁著名的塔尔寺,典型的藏传佛教寺庙,白塔、唐卡、金顶、转轮和宽大的讲经堂。我是着实感受到了十一旅游的狂潮,寺庙里人山人海,很难真正静下心来跟当地的和尚喇嘛聊聊天感受一下他们信仰和文化,只能走马观花地拍照。可惜的是我从不喜欢在宗教场所室内拍照,感觉是对宗教的不尊重,所以也只能拍拍外景。

当然,在寺庙里还是看到了很震撼我的一幕,很多上了年纪的牧民,在我裹得严严实实的时候,穿着露袖的宗教服饰,匍匐在地,连续不断地行跪拜礼。我虽然是个无神主义者,但是我尊重他人的信仰,虽然我也觉得某一些宗教里确实有很值得商榷的教义。

青海湖的景色确实让我惊讶,这边的一切都显得那么苍茫而空旷,如果没有那么多旅游人群的话确实是一片仙境。在湖边我第一次骑了牦牛,比我想像的乖很多,任我肆意撸。平静的湖面倒映着远山和云彩,我这时候突然反应过来“青海”这个名字的由来,或许在那个信息不那么发达的年代,这些住在距离最近的海岸线近2500公里外的牧民,看到这一望无际的湖面,难免会觉得是找到了大海的方向。

我知道青海是著名的长江、黄河、澜沧江三江发源地,沿途的山谷里,到处都是这样的小溪流慢慢从高原流下,我不知道它们会流向哪里,是汇入江海还是湖泊,虽然这次我们没有机会去一睹三江源的风采,或许黄河长江在源头也是这样的小溪。


西出阳关无故人

因为行程的缘故,我们需要在一天内从柴达木盆地内的德令哈赶到这次最主要的目的地敦煌,然后参观当天下午的莫高窟,所以只能很早出发赶路,然后遗憾地没能去著名的阳关、玉门关和汉长城遗址,虽说我一直以来很想亲眼看看这些见证着我们民族兴衰的标志,无奈只能看看别人的照片了。

祁连山脉绵延千里,是青海和甘肃的交界,景色绮丽壮美,蜿蜒巍峨的山峰和雪山耸立在苍茫天地间。

莫高窟是我这趟的最主要目的地,预约票能参观有解说的八个洞窟,和四个没解说的长期开放的窟。从南北朝十六国开始一直到民国,莫高窟汇聚了这1500多年中国文化的瑰宝,各种不同年代和文明的艺术在这里交织碰撞,看的很是过瘾。可惜的是莫高窟的破坏很严重,所以我也特别有责任感,我们做的数字图书馆能一劳永逸地将这些瑰宝保存呈现给后人看。

鸣沙山和月牙泉比我想象中还美丽,确实是塞上江南的一颗明珠。我第一次尝试攀爬沙漠上的山丘,比我想象的要困难多了,一步一滑,如果没有辅助措施确实是寸步难行,不禁佩服当年往来沙漠的使团和商队,是怎么一步步翻阅崇山峻岭前行的。

当然来这骑骆驼是少不了的,这也是我第一次骑这货,一开始非常的颠簸,之后却在沙丘上保持着动态平衡。由于十一黄金周的缘故,沙丘上的骆驼队络绎不绝,延绵好几公里,可能当初丝绸之路最繁忙的时候也不过如此。


七彩丹霞和马蹄寺

我一直吐槽说,我以前知道河西走廊上有武威郡、张掖郡和嘉峪关,但我确实不知道那边有什么值得玩的景点,直到有次在电影开演前的广告上看到了七彩丹霞地质公园的广告,才觉得应该来这一趟。

丹霞地貌山峦上不太有植被,但是这确实是我印象中西北所应该有的样子。

马蹄寺在张掖南边60公里的小山村里,很遗憾没参观到三十三洞窟,因为人实在是太多了。但是从这边能看到祁连雪山和雪山下的村庄。据说河西走廊之所以能水足土肥就是靠着祁连山脉上的冰川融解灌溉

马蹄寺修筑在悬崖之上,从下往上看特别震撼。


甘肃省博物馆

就为了一睹这马踏飞燕图的风采,我们最后一天一早去了兰州甘肃省博物馆。

这个元代的水晶杯是现存唯一一个蓝色的水晶杯,质地非常好,色泽无比漂亮。

长得像恶魔果实的仰韶文化彩陶。

长得像秦岭神树的青铜树。

从小就在书上看到的曾经的亚洲最大——马门溪龙。


西北人

最后我是真的很想吐槽一下西北的交通和城市道路规划建设。感觉整个甘肃省都在大刀阔斧地道路整修,导致整个城市交通一塌糊涂,处处都是断头路。但是就是在这些被圈围起来的道路里,却诞生着这个城市最有代表性的美食味道。 临行前一天晚上,我坐的摊位是一对回族夫妇经营的。我跟老板娘聊了好久。我一直觉得自己是个看人很准的人,我在她脸上看到了一个我们似乎遗失了很久的纯朴的笑脸,让我特别感动。她一脸开心地说起自己的生活,这不禁让我反思了很多,似乎在网络上我们对回民妖魔化了太多,或许他们其中确实有坏人,或许他们的信仰确实有值得商榷之处,但这不能成为我们否定他们每一个人的理由,因为至少在这对夫妇身上,和那些因为突降大雨跟他们一起互帮互助的商贩和路人身上,我看到了那种人内心深处毫无掩饰的善良。

]]>
【转】kaggle数据挖掘比赛基本流程 2017-09-08T00:00:00+00:00 Kaka Chen http://kakack.github.io/2017/09/【转】Kaggle数据挖掘比赛基本流程 转载【干货】Kaggle 数据挖掘比赛经验分享


简介

Kaggle 于 2010 年创立,专注数据科学,机器学习竞赛的举办,是全球最大的数据科学社区和数据竞赛平台。笔者从 2013 年开始,陆续参加了多场 Kaggle上面举办的比赛,相继获得了 CrowdFlower 搜索相关性比赛第一名(1326支队伍)和 HomeDepot 商品搜索相关性比赛第三名(2125支队伍),曾在 Kaggle 数据科学家排行榜排名全球第十,国内第一。笔者目前在腾讯社交与效果广告部任职数据挖掘工程师,负责 Lookalike 相似人群扩展相关工作。此文分享笔者在参加数据挖掘比赛过程中的一点心得体会。


1.Kaggle 基本介绍

Kaggle 于 2010 年创立,专注数据科学,机器学习竞赛的举办,是全球最大的数据科学社区和数据竞赛平台。在 Kaggle 上,企业或者研究机构发布商业和科研难题,悬赏吸引全球的数据科学家,通过众包的方式解决建模问题。而参赛者可以接触到丰富的真实数据,解决实际问题,角逐名次,赢取奖金。诸如 Google,Facebook,Microsoft 等知名科技公司均在 Kaggle 上面举办过数据挖掘比赛。2017年3月,Kaggle 被 Google CloudNext 收购。

1.1 参赛方式

可以以个人或者组队的形式参加比赛。组队人数一般没有限制,但需要在 Merger Deadline 前完成组队。为了能参与到比赛中,需要在 Entry Deadline 前进行至少一次有效提交。最简单地,可以直接提交官方提供的 Sample Submission。关于组队,建议先单独个人进行数据探索和模型构建,以个人身份进行比赛,在比赛后期(譬如离比赛结束还有 2~3 周)再进行组队,以充分发挥组队的效果(类似于模型集成,模型差异性越大,越有可能有助于效果的提升,超越单模型的效果)。当然也可以一开始就组好队,方便分工协作,讨论问题和碰撞火花。

Kaggle 对比赛的公正性相当重视。在比赛中,每个人只允许使用一个账号进行提交。在比赛结束后 1到2 周内,Kaggle 会对使用多账号提交的 Cheater 进行剔除(一般会对 Top 100 的队伍进行 Cheater Detection)。在被剔除者的 Kaggle 个人页面上,该比赛的成绩也会被删除,相当于该选手从没参加过这个比赛。此外,队伍之间也不能私自分享代码或者数据,除非在论坛上面公开发布。

比赛一般只提交测试集的预测结果,无需提交代码。每人(或每个队伍)每天有提交次数的限制,一般为2次或者5次,在 Submission 页面会有提示。

1.2 比赛获奖

Kaggle 比赛奖金丰厚,一般前三名均可以获得奖金。在最近落幕的第二届 National Data Science Bowl 中,总奖金池高达 100W 美刀,其中第一名可以获得 50W 美刀的奖励,即使是第十名也能收获 2.5W 美刀的奖金。

获奖的队伍需要在比赛结束后 1~2 周内,准备好可执行的代码以及 README,算法说明文档等提交给 Kaggle 来进行获奖资格的审核。Kaggle 会邀请获奖队伍在 Kaggle Blog 中发表 Interview,来分享比赛故事和经验心得。对于某些比赛,Kaggle 或者主办方会邀请获奖队伍进行电话/视频会议,获奖队伍进行 Presentation,并与主办方团队进行交流。

1.3 比赛类型

从 Kaggle 提供的官方分类来看,可以划分为以下类型(如下图1所示):

  • Featured:商业或科研难题,奖金一般较为丰厚;
  • Recruitment:比赛的奖励为面试机会;
  • Research:科研和学术性较强的比赛,也会有一定的奖金,一般需要较强的领域和专业知识;
  • Playground:提供一些公开的数据集用于尝试模型和算法;
  • Getting Started:提供一些简单的任务用于熟悉平台和比赛;
  • In Class:用于课堂项目作业或者考试。

从领域归属划分:包含搜索相关性,广告点击率预估,销量预估,贷款违约判定,癌症检测等。

从任务目标划分:包含回归,分类(二分类,多分类,多标签),排序,混合体(分类+回归)等。

从数据载体划分:包含文本,语音,图像和时序序列等。

从特征形式划分:包含原始数据,明文特征,脱敏特征(特征的含义不清楚)等。

1.4 比赛流程

一个数据挖掘比赛的基本流程如下图2所示,具体的模块我将在下一章进行展开陈述。

这里想特别强调的一点是,Kaggle 在计算得分的时候,有Public Leaderboard (LB)和 Private LB 之分。具体而言,参赛选手提交整个测试集的预测结果,Kaggle 使用测试集的一部分计算得分和排名,实时显示在 Public LB上,用于给选手提供及时的反馈和动态展示比赛的进行情况;测试集的剩余部分用于计算参赛选手的最终得分和排名,此即为 Private LB,在比赛结束后会揭晓。用于计算 Public LB 和 Private LB 的数据有不同的划分方式,具体视比赛和数据的类型而定,一般有随机划分,按时间划分或者按一定规则划分。

这个过程可以概括如下图3所示,其目的是避免模型过拟合,以得到泛化能力好的模型。如果不设置 Private LB(即所有的测试数据都用于计算 Public LB),选手不断地从 Public LB(即测试集)中获得反馈,进而调整或筛选模型。这种情况下,测试集实际上是作为验证集参与到模型的构建和调优中来。Public LB上面的效果并非是在真实未知数据上面的效果,不能可靠地反映模型的效果。划分 Public LB 和 Private LB 这样的设置,也在提醒参赛者,我们建模的目标是要获得一个在未知数据上表现良好的模型,而并非仅仅是在已知数据上效果好。


2.数据挖掘比赛基本流程

从上面图2可以看到,做一个数据挖掘比赛,主要包含了数据分析,数据清洗,特征工程,模型训练和验证等四个大的模块,以下来一一对其进行介绍。

2.1 数据分析

数据分析可能涉及以下方面:

◆ 分析特征变量的分布

◇ 特征变量为连续值:如果为长尾分布并且考虑使用线性模型,可以对变量进行幂变换或者对数变换。

◇ 特征变量为离散值:观察每个离散值的频率分布,对于频次较低的特征,可以考虑统一编码为“其他”类别。

◆ 分析目标变量的分布

◇ 目标变量为连续值:查看其值域范围是否较大,如果较大,可以考虑对其进行对数变换,并以变换后的值作为新的目标变量进行建模(在这种情况下,需要对预测结果进行逆变换)。一般情况下,可以对连续变量进行Box-Cox变换。通过变换可以使得模型更好的优化,通常也会带来效果上的提升。

◇ 目标变量为离散值:如果数据分布不平衡,考虑是否需要上采样/下采样;如果目标变量在某个ID上面分布不平衡,在划分本地训练集和验证集的时候,需要考虑分层采样(Stratified Sampling)。

◆ 分析变量之间两两的分布和相关度

◇ 可以用于发现高相关和共线性的特征。

通过对数据进行探索性分析(甚至有些情况下需要肉眼观察样本),还可以有助于启发数据清洗和特征抽取,譬如缺失值和异常值的处理,文本数据是否需要进行拼写纠正等。

2.2 数据清洗

数据清洗是指对提供的原始数据进行一定的加工,使得其方便后续的特征抽取。其与特征抽取的界限有时也没有那么明确。常用的数据清洗一般包括:

◆ 数据的拼接

◇ 提供的数据散落在多个文件,需要根据相应的键值进行数据的拼接。

◆ 特征缺失值的处理

◇ 特征值为连续值:按不同的分布类型对缺失值进行补全:偏正态分布,使用均值代替,可以保持数据的均值;偏长尾分布,使用中值代替,避免受 outlier 的影响;

◇ 特征值为离散值:使用众数代替。

◆ 文本数据的清洗

◇ 在比赛当中,如果数据包含文本,往往需要进行大量的数据清洗工作。如去除HTML 标签,分词,拼写纠正, 同义词替换,去除停词,抽词干,数字和单位格式统一等。

2.3 特征工程

有一种说法是,特征决定了效果的上限,而不同模型只是以不同的方式或不同的程度来逼近这个上限。这样来看,好的特征输入对于模型的效果至关重要,正所谓”Garbage in, garbage out”。要做好特征工程,往往跟领域知识和对问题的理解程度有很大的关系,也跟一个人的经验相关。特征工程的做法也是Case by Case,以下就一些点,谈谈自己的一些看法。

2.3.1 特征变换

主要针对一些长尾分布的特征,需要进行幂变换或者对数变换,使得模型(LR或者DNN)能更好的优化。需要注意的是,Random Forest 和 GBDT 等模型对单调的函数变换不敏感。其原因在于树模型在求解分裂点的时候,只考虑排序分位点。

2.3.2 特征编码

对于离散的类别特征,往往需要进行必要的特征转换/编码才能将其作为特征输入到模型中。常用的编码方式有 LabelEncoder,OneHotEncoder(sklearn里面的接口)。譬如对于”性别”这个特征(取值为男性和女性),使用这两种方式可以分别编码为{0,1}和{[1,0], [0,1]}。

对于取值较多(如几十万)的类别特征(ID特征),直接进行OneHotEncoder编码会导致特征矩阵非常巨大,影响模型效果。可以使用如下的方式进行处理:

◆ 统计每个取值在样本中出现的频率,取 Top N 的取值进行 One-hot 编码,剩下的类别分到“其他“类目下,其中 N 需要根据模型效果进行调优;

◆ 统计每个 ID 特征的一些统计量(譬如历史平均点击率,历史平均浏览率)等代替该 ID 取值作为特征,具体可以参考 Avazu 点击率预估比赛第二名的获奖方案;

◆ 参考 word2vec 的方式,将每个类别特征的取值映射到一个连续的向量,对这个向量进行初始化,跟模型一起训练。训练结束后,可以同时得到每个ID的Embedding。具体的使用方式,可以参考 Rossmann 销量预估竞赛第三名的获奖方案,entron/entity-embedding-rossmann。

对于 Random Forest 和 GBDT 等模型,如果类别特征存在较多的取值,可以直接使用 LabelEncoder 后的结果作为特征。

2.4 模型训练和验证

2.4.1 模型选择

在处理好特征后,我们可以进行模型的训练和验证。

◆ 对于稀疏型特征(如文本特征,One-hot的ID类特征),我们一般使用线性模型,譬如 Linear Regression 或者 Logistic Regression。Random Forest 和 GBDT 等树模型不太适用于稀疏的特征,但可以先对特征进行降维(如PCA,SVD/LSA等),再使用这些特征。稀疏特征直接输入 DNN 会导致网络 weight 较多,不利于优化,也可以考虑先降维,或者对 ID 类特征使用 Embedding 的方式;

◆ 对于稠密型特征,推荐使用 XGBoost 进行建模,简单易用效果好;

◆ 数据中既有稀疏特征,又有稠密特征,可以考虑使用线性模型对稀疏特征进行建模,将其输出与稠密特征一起再输入 XGBoost/DNN 建模,具体可以参考2.5.2节 Stacking 部分。

2.4.2 调参和模型验证

对于选定的特征和模型,我们往往还需要对模型进行超参数的调优,才能获得比较理想的效果。调参一般可以概括为以下三个步骤:

一,训练集和验证集的划分。根据比赛提供的训练集和测试集,模拟其划分方式对训练集进行划分为本地训练集和本地验证集。划分的方式视具体比赛和数据而定,常用的方式有:

a) 随机划分:譬如随机采样 70% 作为训练集,剩余的 30% 作为测试集。在这种情况下,本地可以采用 KFold 或者 Stratified KFold 的方法来构造训练集和验证集。
b) 按时间划分:一般对应于时序序列数据,譬如取前 7 天数据作为训练集,后 1 天数据作为测试集。这种情况下,划分本地训练集和验证集也需要按时间先后划分。常见的错误方式是随机划分,这种划分方式可能会导致模型效果被高估。
c) 按某些规则划分:在 HomeDepot 搜索相关性比赛中,训练集和测试集中的 Query 集合并非完全重合,两者只有部分交集。而在另外一个相似的比赛中(CrowdFlower 搜索相关性比赛),训练集和测试集具有完全一致的 Query 集合。对于 HomeDepot 这个比赛中,训练集和验证集数据的划分,需要考虑 Query 集合并非完全重合这个情况,其中的一种方法可以参考第三名的获奖方案,https://github.com/ChenglongChen/Kaggle_HomeDepot。

二,指定参数空间。在指定参数空间的时候,需要对模型参数以及其如何影响模型的效果有一定的了解,才能指定出合理的参数空间。譬如DNN或者XGBoost中学习率这个参数,一般就选 0.01 左右就 OK 了(太大可能会导致优化算法错过最优化点,太小导致优化收敛过慢)。再如 Random Forest,一般设定树的棵数范围为 100~200 就能有不错的效果,当然也有人固定数棵数为 500,然后只调整其他的超参数。

三,按照一定的方法进行参数搜索。常用的参数搜索方法有,Grid Search,Random Search以及一些自动化的方法(如 Hyperopt)。其中,Hyperopt 的方法,根据历史已经评估过的参数组合的效果,来推测本次评估使用哪个参数组合更有可能获得更好的效果。有关这些方法的介绍和对比,可以参考文献 [2]。

2.4.3 适当利用 Public LB 的反馈

在2.4.2节中我们提到本地验证(Local Validation)结果,当将预测结果提交到 Kaggle 上时,我们还会接收到 Public LB 的反馈结果。如果这两个结果的变化趋势是一致的,如 Local Validation 有提升,Public LB 也有提升,我们可以借助 Local Validation 的变化来感知模型的演进情况,而无需靠大量的 Submission。如果两者的变化趋势不一致,需要考虑2.4.2节中提及的本地训练集和验证集的划分方式,是否跟训练集和测试集的划分方式一致。

另外,在以下一些情况下,往往 Public LB 反馈亦会提供有用信息,适当地使用这些反馈也许会给你带来优势。如图4所示,(a)和(b)表示数据与时间没有明显的关系(如图像分类),(c)和(d)表示数据随时间变化(如销量预估中的时序序列)。(a)和(b)的区别在于,训练集样本数相对于 Public LB 的量级大小,其中(a)中训练集样本数远超于 Public LB 的样本数,这种情况下基于训练集的 Local Validation 更可靠;而(b)中,训练集数目与 Public LB 相当,这种情况下,可以结合 Public LB 的反馈来指导模型的选择。一种融合的方式是根据 Local Validation 和 Public LB 的样本数目,按比例进行加权。譬如评估标准为正确率,Local Validation 的样本数为 N_l,正确率为 A_l;Public LB 的样本数为 N_p,正确率为 A_p。则可以使用融合后的指标:(N_l * A_l + N_p * A_p)/(N_l + N_p),来进行模型的筛选。对于(c)和(d),由于数据分布跟时间相关,很有必要使用 Public LB 的反馈来进行模型的选择,尤其对于(c)图所示的情况。

2.5 模型集成

如果想在比赛中获得名次,几乎都要进行模型集成(组队也是一种模型集成)。关于模型集成的介绍,已经有比较好的博文了,可以参考 [3]。在这里,我简单介绍下常用的方法,以及个人的一些经验。

2.5.1 Averaging 和 Voting

直接对多个模型的预测结果求平均或者投票。对于目标变量为连续值的任务,使用平均;对于目标变量为离散值的任务,使用投票的方式。

2.5.2 Stacking

上图展示了使用 5-Fold 进行一次 Stacking 的过程(当然在其上可以再叠加 Stage 2, Stage 3 等)。其主要的步骤如下:

  1. 数据集划分。将训练数据按照5-Fold进行划分(如果数据跟时间有关,需要按时间划分,更一般的划分方式请参考3.4.2节,这里不再赘述);

  2. 基础模型训练 I(如图5第一行左半部分所示)。按照交叉验证(Cross Validation)的方法,在训练集(Training Fold)上面训练模型(如图灰色部分所示),并在验证集(Validation Fold)上面做预测,得到预测结果(如图黄色部分所示)。最后综合得到整个训练集上面的预测结果(如图第一个黄色部分的CV Prediction所示)。

  3. 基础模型训练 II(如图5第二和三行左半部分所示)。在全量的训练集上训练模型(如图第二行灰色部分所示),并在测试集上面做预测,得到预测结果(如图第三行虚线后绿色部分所示)。

  4. Stage 1 模型集成训练 I(如图5第一行右半部分所示)。将步骤 2 中得到的 CV Prediction 当作新的训练集,按照步骤 2 可以得到 Stage 1模型集成的 CV Prediction。

  5. Stage 1 模型集成训练 II(如图5第二和三行右半部分所示)。将步骤 2 中得到的 CV Prediction 当作新的训练集和步骤 3 中得到的 Prediction 当作新的测试集,按照步骤 3 可以得到 Stage 1 模型集成的测试集 Prediction。此为 Stage 1 的输出,可以提交至 Kaggle 验证其效果。

在图5中,基础模型只展示了一个,而实际应用中,基础模型可以多种多样,如SVM,DNN,XGBoost 等。也可以相同的模型,不同的参数,或者不同的样本权重。重复4和5两个步骤,可以相继叠加 Stage 2, Stage 3 等模型。

2.5.3 Blending

Blending 与 Stacking 类似,但单独留出一部分数据(如 20%)用于训练 Stage X 模型。

2.5.4 Bagging Ensemble Selection

Bagging Ensemble Selection [5] 是我在 CrowdFlower 搜索相关性比赛中使用的方法,其主要的优点在于可以以优化任意的指标来进行模型集成。这些指标可以是可导的(如 LogLoss 等)和不可导的(如正确率,AUC,Quadratic Weighted Kappa等)。它是一个前向贪婪算法,存在过拟合的可能性,作者在文献 [5] 中提出了一系列的方法(如 Bagging)来降低这种风险,稳定集成模型的性能。使用这个方法,需要有成百上千的基础模型。为此,在 CrowdFlower 的比赛中,我把在调参过程中所有的中间模型以及相应的预测结果保留下来,作为基础模型。这样做的好处是,不仅仅能够找到最优的单模型(Best Single Model),而且所有的中间模型还可以参与模型集成,进一步提升效果。

2.6 自动化框架

从上面的介绍可以看到,做一个数据挖掘比赛涉及到的模块非常多,若有一个较自动化的框架会使得整个过程更加的高效。在 CrowdFlower 比赛较前期,我对整一个项目的代码架构进行了重构,抽象出来特征工程,模型调参和验证,以及模型集成等三大模块,极大的提高了尝试新特征,新模型的效率,也是我最终能斩获名次的一个有利因素。这份代码开源在 Github 上面,目前是 Github 有关 Kaggle 竞赛解决方案的 Most Stars,地址:ChenglongChen/Kaggle_CrowdFlower

其主要包含以下部分:

一,模块化特征工程

a) 接口统一,只需写少量的代码就能够生成新的特征;

b) 自动将单独的特征拼接成特征矩阵。

二,自动化模型调参和验证

a) 自定义训练集和验证集的划分方法;

b) 使用 Grid Search / Hyperopt 等方法,对特定的模型在指定的参数空间进行调优,并记录最佳的模型参数以及相应的性能。

三,自动化模型集成

a) 对于指定的基础模型,按照一定的方法(如Averaging/Stacking/Blending 等)生成集成模型。 

3.Kaggle竞赛方案盘点

到目前为止,Kaggle 平台上面已经举办了大大小小不同的赛事,覆盖图像分类,销量预估,搜索相关性,点击率预估等应用场景。在不少的比赛中,获胜者都会把自己的方案开源出来,并且非常乐于分享比赛经验和技巧心得。这些开源方案和经验分享对于广大的新手和老手来说,是入门和进阶非常好的参考资料。以下笔者结合自身的背景和兴趣,对不同场景的竞赛开源方案作一个简单的盘点,总结其常用的方法和工具,以期启发思路。

3.1 图像分类

3.1.1 任务名称

National Data Science Bowl

3.1.2 任务详情

随着深度学习在视觉图像领域获得巨大成功,Kaggle 上面出现了越来越多跟视觉图像相关的比赛。这些比赛的发布吸引了众多参赛选手,探索基于深度学习的方法来解决垂直领域的图像问题。NDSB就是其中一个比较早期的图像分类相关的比赛。这个比赛的目标是利用提供的大量的海洋浮游生物的二值图像,通过构建模型,从而实现自动分类。

3.1.3 获奖方案

● 1st place:Cyclic Pooling + Rolling Feature Maps + Unsupervised and Semi-Supervised Approaches。值得一提的是,这个队伍的主力队员也是Galaxy Zoo行星图像分类比赛的第一名,其也是Theano中基于FFT的Fast Conv的开发者。在两次比赛中,使用的都是 Theano,而且用的非常溜。方案链接:Classifying plankton with deep neural networks

● 2nd place:Deep CNN designing theory + VGG-like model + RReLU。这个队伍阵容也相当强大,有前MSRA 的研究员Xudong Cao,还有大神Tianqi Chen,Naiyan Wang,Bing XU等。Tianqi 等大神当时使用的是 CXXNet(MXNet 的前身),也在这个比赛中进行了推广。Tianqi 大神另外一个大名鼎鼎的作品就是 XGBoost,现在 Kaggle 上面几乎每场比赛的 Top 10 队伍都会使用。方案链接:National Data Science Bowl

● 17th place:Realtime data augmentation + BN + PReLU。方案链接:ChenglongChen/caffe-windows

3.1.4 常用工具

▲ Theano: Welcome - Theano 0.9.0 documentation

▲ Keras: Keras Documentation

▲ Cuda-convnet2: akrizhevsky/cuda-convnet2

▲ Caffe: [Caffe Deep Learning Framework](http://link.zhihu.com/?target=http%3A//caffe.berkeleyvision.org/)

▲ CXXNET: dmlc/cxxnet

▲ MXNet: dmlc/mxnet

▲ PaddlePaddle: PaddlePaddle —- PArallel Distributed Deep LEarning

3.2 销量预估

3.2.1 任务名称

Walmart Recruiting - Store Sales Forecasting

3.2.2 任务详情

Walmart 提供 2010-02-05 到 2012-11-01 期间的周销售记录作为训练数据,需要参赛选手建立模型预测 2012-11-02 到 2013-07-26 周销售量。比赛提供的特征数据包含:Store ID, Department ID, CPI,气温,汽油价格,失业率,是否节假日等。

3.2.3 获奖方案

● 1st place:Time series forecasting method: stlf + arima + ets。主要是基于时序序列的统计方法,大量使用了 Rob J Hyndman 的 forecast R 包。方案链接:Walmart Recruiting - Store Sales Forecasting

● 2nd place:Time series forecasting + ML: arima + RF + LR + PCR。时序序列的统计方法+传统机器学习方法的混合;方案链接:Walmart Recruiting - Store Sales Forecasting

● 16th place:Feature engineering + GBM。方案链接:ChenglongChen/Kaggle_Walmart-Recruiting-Store-Sales-Forecasting

3.2.4 常用工具

▲ R forecast package: https://cran.r-project.org/web/packages/forecast/index.html

▲ R GBM package: https://cran.r-project.org/web/packages/gbm/index.html

3.3 搜索相关性

3.3.1 任务名称

CrowdFlower Search Results Relevance

3.3.2 任务详情

比赛要求选手利用约几万个 (query, title, description) 元组的数据作为训练样本,构建模型预测其相关性打分 {1, 2, 3, 4}。比赛提供了 query, title和description的原始文本数据。比赛使用 Quadratic Weighted Kappa 作为评估标准,使得该任务有别于常见的回归和分类任务。

3.3.3 获奖方案

● 1st place:Data Cleaning + Feature Engineering + Base Model + Ensemble。对原始文本数据进行清洗后,提取了属性特征,距离特征和基于分组的统计特征等大量的特征,使用了不同的目标函数训练不同的模型(回归,分类,排序等),最后使用模型集成的方法对不同模型的预测结果进行融合。方案链接:ChenglongChen/Kaggle_CrowdFlower

● 2nd place:A Similar Workflow

● 3rd place: A Similar Workflow

3.3.4 常用工具

▲ NLTK: Natural Language Toolkit

▲ Gensim: gensim: topic modelling for humans

▲ XGBoost: dmlc/xgboost

▲ RGF: baidu/fast_rgf

3.4 点击率预估 I

3.4.1 任务名称

Criteo Display Advertising Challenge

3.4.2 任务详情

经典的点击率预估比赛。该比赛中提供了7天的训练数据,1 天的测试数据。其中有13 个整数特征,26 个类别特征,均脱敏,因此无法知道具体特征含义。

3.4.3 获奖方案

● 1st place:GBDT 特征编码 + FFM。台大的队伍,借鉴了Facebook的方案 [6],使用 GBDT 对特征进行编码,然后将编码后的特征以及其他特征输入到 Field-aware Factorization Machine(FFM) 中进行建模。方案链接:Display Advertising Challenge Kaggle
● 3rd place:Quadratic Feature Generation + FTRL。传统特征工程和 FTRL 线性模型的结合。方案链接:Display Advertising Challenge Kaggle

● 4th place:Feature Engineering + Sparse DNN

3.4.4 常用工具

▲ Vowpal Wabbit: JohnLangford/vowpal_wabbit

▲ XGBoost: dmlc/xgboost

▲ LIBFFM: LIBFFM: A Library for Field-aware Factorization Machines

3.5 点击率预估 II

3.5.1 任务名称

Avazu Click-Through Rate Prediction

3.5.2 任务详情

点击率预估比赛。提供了 10 天的训练数据,1 天的测试数据,并且提供时间,banner 位置,site, app, device 特征等,8个脱敏类别特征。

3.5.3 获奖方案

● 1st place:Feature Engineering + FFM + Ensemble。还是台大的队伍,这次比赛,他们大量使用了 FFM,并只基于 FFM 进行集成。方案链接:Click-Through Rate Prediction Kaggle

● 2nd place:Feature Engineering + GBDT 特征编码 + FFM + Blending。Owenzhang(曾经长时间雄霸 Kaggle 排行榜第一)的竞赛方案。Owenzhang 的特征工程做得非常有参考价值。方案链接:owenzhang/kaggle-avazu

3.5.4 常用工具

▲ LIBFFM: LIBFFM: A Library for Field-aware Factorization Machines

▲ XGBoost: dmlc/xgboost


4.参考资料

  • [1] Owenzhang 的分享: Tips for Data Science Competitions
  • [2] Algorithms for Hyper-Parameter Optimization
  • [3] MLWave博客:Kaggle Ensembling Guide
  • [4] Jeong-Yoon Lee 的分享:Winning Data Science Competitions
  • [5] Ensemble Selection from Libraries of Models
  • [6] Practical Lessons from Predicting Clicks on Ads at Facebook
]]>
Building a deep neural network 2017-09-07T00:00:00+00:00 Kaka Chen http://kakack.github.io/2017/09/Building a Deep Neural Network Build a Deep Neural Network

1 - Package

  • numpy Python基础科学计算库
  • matplotlib Python绘图库
  • dnn_utils 提供一些必要的函数
  • testCases 为自己的函数提供一些测试集
  • np.random.seed(1) 随机种子
import numpy as np
import h5py
import matplotlib.pyplot as plt
from testCases_v2 import *
from dnn_utils_v2 import sigmoid, sigmoid_backward, relu, relu_backward

%matplotlib inline
plt.rcParams['figure.figsize'] = (5.0, 4.0) # set default size of plots
plt.rcParams['image.interpolation'] = 'nearest'
plt.rcParams['image.cmap'] = 'gray'

%load_ext autoreload
%autoreload 2

np.random.seed(1)

2 - 大纲

  • 为一个2-layer的网络和一个L-layer的神经网络初始化参数
  • 实现Forward propagation模块:
    • 计算一层的线性部分(记Z[l])
    • 获得激活函数(relu/sigmoid)
    • 将上述两步(linear->activation)合并入一个新的Forward函数
    • 将[linear->relu]Forward propagation结合L-1次(在layer 1上),并在后面添加一个[linear->sigmoid]
  • 计算损失
  • 实现Backward propagation模块:
    • 计算backward propagation部分中的线性部分
    • 得到激活函数的梯度(relu_backward/sigmoid_backward)
    • 将上述两步合并到一个新的backward函数[linear->activation]
    • 将[linear->relu] backward L-1次,并在后面添加一个[linear->sigmoid]到一个新的L-model_backward函数中
  • 最终更新参数


3 - 初始化

3.1 2-Layer神经网络

  • 模型结构: linear->relu->linear->sigmoid
  • np.random.randn(shape)*0.01初始化权重w
  • np.zeros(shape)初始化偏移量b
# GRADED FUNCTION: initialize_parameters

def initialize_parameters(n_x, n_h, n_y):
    """
    Argument:
    n_x -- size of the input layer
    n_h -- size of the hidden layer
    n_y -- size of the output layer
    
    Returns:
    parameters -- python dictionary containing your parameters:
                    W1 -- weight matrix of shape (n_h, n_x)
                    b1 -- bias vector of shape (n_h, 1)
                    W2 -- weight matrix of shape (n_y, n_h)
                    b2 -- bias vector of shape (n_y, 1)
    """
    
    np.random.seed(1)
    
    W1 = np.random.randn(n_h, n_x) * 0.01
    b1 = np.zeros((n_h, 1))
    W2 = np.random.randn(n_y, n_h) * 0.01
    b2 = np.zeros((n_y, 1))
    
    assert(W1.shape == (n_h, n_x))
    assert(b1.shape == (n_h, 1))
    assert(W2.shape == (n_y, n_h))
    assert(b2.shape == (n_y, 1))
    
    parameters = {"W1": W1,
                  "b1": b1,
                  "W2": W2,
                  "b2": b2}
    
    return parameters    
parameters = initialize_parameters(2,2,1)
print("W1 = " + str(parameters["W1"]))
print("b1 = " + str(parameters["b1"]))
print("W2 = " + str(parameters["W2"]))
print("b2 = " + str(parameters["b2"]))
**W1** [[ 0.01624345 -0.00611756] [-0.00528172 -0.01072969]]
**b1** [[ 0.] [ 0.]]
**W2** [[ 0.00865408 -0.02301539]]
**b2** [[ 0.]]

各层的W、b、激活函数尺寸大小

在Python中计算WX+b

  • 这个模型的结构[linear->relu]x(L-1)->Linear->Sigmoid
  • 用random初始化权重w矩阵 np.random.rand(shape) * 0.01
  • 用0初始化偏移量b,np.zeros(shape)
  • 将不同层的units个数n[l]存在一个变量layer_dims
# GRADED FUNCTION: initialize_parameters_deep

def initialize_parameters_deep(layer_dims):
    """
    Arguments:
    layer_dims -- python array (list) containing the dimensions of each layer in our network
    
    Returns:
    parameters -- python dictionary containing your parameters "W1", "b1", ..., "WL", "bL":
                    Wl -- weight matrix of shape (layer_dims[l], layer_dims[l-1])
                    bl -- bias vector of shape (layer_dims[l], 1)
    """
    
    np.random.seed(3)
    parameters = {}
    L = len(layer_dims)            # number of layers in the network

    for l in range(1, L):

        parameters['W' + str(l)] = np.random.randn(layer_dims[l], layer_dims[l-1]) * 0.01
        parameters['b' + str(l)] = np.zeros((layer_dims[l], 1))
        
        assert(parameters['W' + str(l)].shape == (layer_dims[l], layer_dims[l-1]))
        assert(parameters['b' + str(l)].shape == (layer_dims[l], 1))
      
    return parameters
parameters = initialize_parameters_deep([5,4,3])
print("W1 = " + str(parameters["W1"]))
print("b1 = " + str(parameters["b1"]))
print("W2 = " + str(parameters["W2"]))
print("b2 = " + str(parameters["b2"]))
**W1** [[ 0.01788628 0.0043651 0.00096497 -0.01863493 -0.00277388] [-0.00354759 -0.00082741 -0.00627001 -0.00043818 -0.00477218] [-0.01313865 0.00884622 0.00881318 0.01709573 0.00050034] [-0.00404677 -0.0054536 -0.01546477 0.00982367 -0.01101068]]
**b1** [[ 0.] [ 0.] [ 0.] [ 0.]]
**W2** [[-0.01185047 -0.0020565 0.01486148 0.00236716] [-0.01023785 -0.00712993 0.00625245 -0.00160513] [-0.00768836 -0.00230031 0.00745056 0.01976111]]
**b2** [[ 0.] [ 0.] [ 0.]]

4 - Forward Propagation模块

4.1 Linear Forward

  • LINEAR
  • LINEAR -> ACTIVATION where ACTIVATION will be either ReLU or Sigmoid.
  • [LINEAR -> RELU] ×× (L-1) -> LINEAR -> SIGMOID (whole model)

Linear Forward模块计算这个等式:

其中:

# GRADED FUNCTION: linear_forward

def linear_forward(A, W, b):
    """
    Implement the linear part of a layer's forward propagation.

    Arguments:
    A -- activations from previous layer (or input data): (size of previous layer, number of examples)
    W -- weights matrix: numpy array of shape (size of current layer, size of previous layer)
    b -- bias vector, numpy array of shape (size of the current layer, 1)

    Returns:
    Z -- the input of the activation function, also called pre-activation parameter 
    cache -- a python dictionary containing "A", "W" and "b" ; stored for computing the backward pass efficiently
    """
    
    Z = np.dot(W, A) + b
    
    assert(Z.shape == (W.shape[0], A.shape[1]))
    cache = (A, W, b)
    
    return Z, cache
A, W, b = linear_forward_test_case()

Z, linear_cache = linear_forward(A, W, b)
print("Z = " + str(Z))

# Z = [[ 3.26295337 -1.23429987]]

4.2 Linear-Activation-Forward

  • Sigmoid

运行:A, activation_cache = sigmoid(Z)

  • ReLu

运行A, activation_cache = relu(Z)

# GRADED FUNCTION: linear_activation_forward

def linear_activation_forward(A_prev, W, b, activation):
    """
    Implement the forward propagation for the LINEAR->ACTIVATION layer

    Arguments:
    A_prev -- activations from previous layer (or input data): (size of previous layer, number of examples)
    W -- weights matrix: numpy array of shape (size of current layer, size of previous layer)
    b -- bias vector, numpy array of shape (size of the current layer, 1)
    activation -- the activation to be used in this layer, stored as a text string: "sigmoid" or "relu"

    Returns:
    A -- the output of the activation function, also called the post-activation value 
    cache -- a python dictionary containing "linear_cache" and "activation_cache";
             stored for computing the backward pass efficiently
    """
    
    if activation == "sigmoid":
        # Inputs: "A_prev, W, b". Outputs: "A, activation_cache".

        Z, linear_cache = np.dot(W, A_prev) + b, (A_prev, W, b)
        A, activation_cache = sigmoid(Z)
    
    elif activation == "relu":
        # Inputs: "A_prev, W, b". Outputs: "A, activation_cache".

        Z, linear_cache = np.dot(W, A_prev) + b, (A_prev, W, b)
        A, activation_cache = relu(Z)
    
    assert (A.shape == (W.shape[0], A_prev.shape[1]))
    cache = (linear_cache, activation_cache)

    return A, cache
A_prev, W, b = linear_activation_forward_test_case()

A, linear_activation_cache = linear_activation_forward(A_prev, W, b, activation = "sigmoid")
print("With sigmoid: A = " + str(A))

A, linear_activation_cache = linear_activation_forward(A_prev, W, b, activation = "relu")
print("With ReLU: A = " + str(A))

# With sigmoid: A = [[ 0.96890023  0.11013289]]
# With ReLU: A = [[ 3.43896131  0.        ]]

4.3 L-Layer-Model

# GRADED FUNCTION: L_model_forward

def L_model_forward(X, parameters):
    """
    Implement forward propagation for the [LINEAR->RELU]*(L-1)->LINEAR->SIGMOID computation
    
    Arguments:
    X -- data, numpy array of shape (input size, number of examples)
    parameters -- output of initialize_parameters_deep()
    
    Returns:
    AL -- last post-activation value
    caches -- list of caches containing:
                every cache of linear_relu_forward() (there are L-1 of them, indexed from 0 to L-2)
                the cache of linear_sigmoid_forward() (there is one, indexed L-1)
    """

    caches = []
    A = X
    L = len(parameters) // 2                  # number of layers in the neural network
    
    # Implement [LINEAR -> RELU]*(L-1). Add "cache" to the "caches" list.
    for l in range(1, L):
        A_prev = A 

        A, cache = linear_activation_forward(A_prev, parameters['W' + str(l)], parameters['b' + str(l)], activation = "relu")
        caches.append(cache)
    
    # Implement LINEAR -> SIGMOID. Add "cache" to the "caches" list.

    AL, cache = linear_activation_forward(A, parameters['W' + str(L)], parameters['b' + str(L)], activation = "sigmoid")
    caches.append(cache)
        
    assert(AL.shape == (1,X.shape[1]))
            
    return AL, caches
X, parameters = L_model_forward_test_case()
AL, caches = L_model_forward(X, parameters)
print("AL = " + str(AL))
print("Length of caches list = " + str(len(caches)))

# AL = [[ 0.17007265  0.2524272 ]]
# Length of caches list = 2

5 - Cost Function

 # GRADED FUNCTION: compute_cost
def compute_cost(AL, Y):
    """
    Implement the cost function defined by equation (7).
    Arguments:
    AL -- probability vector corresponding to your label predictions, shape (1, number of examples)
    Y -- true "label" vector (for example: containing 0 if non-cat, 1 if cat), shape (1, number of examples)
    Returns:
    cost -- cross-entropy cost
    """
    
    m = Y.shape[1]

    # Compute loss from aL and y.

    cost = -1./m * np.sum(Y * np.log(AL) + (1 - Y) * np.log(1-AL))
    
    cost = np.squeeze(cost)      # To make sure your cost's shape is what we expect (e.g. this turns [[17]] into 17).
    assert(cost.shape == ())
    
    return cost
 Y, AL = compute_cost_test_case()

print("cost = " + str(compute_cost(AL, Y)))

# cost = 0.414931599615

6 - Backward propagation module

  • LINEAR backward
  • LINEAR -> ACTIVATION backward where ACTIVATION computes the derivative of either the ReLU or sigmoid activation
  • [LINEAR -> RELU] ×× (L-1) -> LINEAR -> SIGMOID backward (whole model)

6.1 Linear Backward

# GRADED FUNCTION: linear_backward

def linear_backward(dZ, cache):
    """
    Implement the linear portion of backward propagation for a single layer (layer l)

    Arguments:
    dZ -- Gradient of the cost with respect to the linear output (of current layer l)
    cache -- tuple of values (A_prev, W, b) coming from the forward propagation in the current layer

    Returns:
    dA_prev -- Gradient of the cost with respect to the activation (of the previous layer l-1), same shape as A_prev
    dW -- Gradient of the cost with respect to W (current layer l), same shape as W
    db -- Gradient of the cost with respect to b (current layer l), same shape as b
    """
    A_prev, W, b = cache
    m = A_prev.shape[1]

    dW = 1./m * np.dot(dZ, A_prev.T)
    db = 1./m * np.sum(np.matrix(dZ), axis = 1)
    dA_prev = np.dot(W.T, dZ)
    
    assert (dA_prev.shape == A_prev.shape)
    assert (dW.shape == W.shape)
    assert (db.shape == b.shape)
    
    return dA_prev, dW, db
# Set up some test inputs
dZ, linear_cache = linear_backward_test_case()

dA_prev, dW, db = linear_backward(dZ, linear_cache)
print ("dA_prev = "+ str(dA_prev))
print ("dW = " + str(dW))
print ("db = " + str(db))

# dA_prev = [[ 0.51822968 -0.19517421]
#  [-0.40506361  0.15255393]
#  [ 2.37496825 -0.89445391]]
# dW = [[-0.10076895  1.40685096  1.64992505]]
# db = [[ 0.50629448]]

6.2 Linear-Activation-Function

  • sigmoid_backwarddZ = sigmoid_backward(dA, activation_cache)
  • relu_backwarddZ = relu_backward(dA, activation_cache)

计算:

# GRADED FUNCTION: linear_activation_backward

def linear_activation_backward(dA, cache, activation):
    """
    Implement the backward propagation for the LINEAR->ACTIVATION layer.
    
    Arguments:
    dA -- post-activation gradient for current layer l 
    cache -- tuple of values (linear_cache, activation_cache) we store for computing backward propagation efficiently
    activation -- the activation to be used in this layer, stored as a text string: "sigmoid" or "relu"
    
    Returns:
    dA_prev -- Gradient of the cost with respect to the activation (of the previous layer l-1), same shape as A_prev
    dW -- Gradient of the cost with respect to W (current layer l), same shape as W
    db -- Gradient of the cost with respect to b (current layer l), same shape as b
    """
    linear_cache, activation_cache = cache
    
    if activation == "relu":
        dZ = relu_backward(dA, activation_cache)
        dA_prev, dW, db = linear_backward(dZ, linear_cache)
        
    elif activation == "sigmoid":
        dZ = sigmoid_backward(dA, activation_cache)
        dA_prev, dW, db = linear_backward(dZ, linear_cache)
    
    return dA_prev, dW, db
AL, linear_activation_cache = linear_activation_backward_test_case()

dA_prev, dW, db = linear_activation_backward(AL, linear_activation_cache, activation = "sigmoid")
print ("sigmoid:")
print ("dA_prev = "+ str(dA_prev))
print ("dW = " + str(dW))
print ("db = " + str(db) + "\n")

dA_prev, dW, db = linear_activation_backward(AL, linear_activation_cache, activation = "relu")
print ("relu:")
print ("dA_prev = "+ str(dA_prev))
print ("dW = " + str(dW))
print ("db = " + str(db))

6.3 L-Model Backward

首先计算dAL:

dAL = - (np.divide(Y, AL) - np.divide(1 - Y, 1 - AL)) 

# derivative of cost with respect to AL

将dA、dW和db都存在grad dictionary里:

# GRADED FUNCTION: L_model_backward

def L_model_backward(AL, Y, caches):
    """
    Implement the backward propagation for the [LINEAR->RELU] * (L-1) -> LINEAR -> SIGMOID group
    
    Arguments:
    AL -- probability vector, output of the forward propagation (L_model_forward())
    Y -- true "label" vector (containing 0 if non-cat, 1 if cat)
    caches -- list of caches containing:
                every cache of linear_activation_forward() with "relu" (it's caches[l], for l in range(L-1) i.e l = 0...L-2)
                the cache of linear_activation_forward() with "sigmoid" (it's caches[L-1])
    
    Returns:
    grads -- A dictionary with the gradients
             grads["dA" + str(l)] = ... 
             grads["dW" + str(l)] = ...
             grads["db" + str(l)] = ... 
    """
    grads = {}
    L = len(caches) # the number of layers
    m = AL.shape[1]
    Y = Y.reshape(AL.shape) # after this line, Y is the same shape as AL
    
    # Initializing the backpropagation

    dAL = - (np.divide(Y, AL) - np.divide(1 - Y, 1 - AL))
    
    # Lth layer (SIGMOID -> LINEAR) gradients. Inputs: "AL, Y, caches". Outputs: "grads["dAL"], grads["dWL"], grads["dbL"]

    current_cache = caches[L-1]
    grads["dA" + str(L)], grads["dW" + str(L)], grads["db" + str(L)] = linear_activation_backward(dAL, current_cache, activation = 'sigmoid')
    
    for l in reversed(range(L-1)):
        # lth layer: (RELU -> LINEAR) gradients.
        # Inputs: "grads["dA" + str(l + 2)], caches". Outputs: "grads["dA" + str(l + 1)] , grads["dW" + str(l + 1)] , grads["db" + str(l + 1)] 

        current_cache = caches[L - 2 -l]
        dA_prev_temp, dW_temp, db_temp = linear_activation_backward(grads["dA" + str(L)], current_cache, activation = 'relu')
        grads["dA" + str(l + 1)] = dA_prev_temp
        grads["dW" + str(l + 1)] = dW_temp
        grads["db" + str(l + 1)] = db_temp

    return grads
AL, Y_assess, caches = L_model_backward_test_case()
grads = L_model_backward(AL, Y_assess, caches)
print ("dW1 = "+ str(grads["dW1"]))
print ("db1 = "+ str(grads["db1"]))
print ("dA1 = "+ str(grads["dA1"]))

6.4 更新参数

# GRADED FUNCTION: update_parameters

def update_parameters(parameters, grads, learning_rate):
    """
    Update parameters using gradient descent
    
    Arguments:
    parameters -- python dictionary containing your parameters 
    grads -- python dictionary containing your gradients, output of L_model_backward
    
    Returns:
    parameters -- python dictionary containing your updated parameters 
                  parameters["W" + str(l)] = ... 
                  parameters["b" + str(l)] = ...
    """
    
    L = len(parameters) // 2 # number of layers in the neural network

    # Update rule for each parameter. Use a for loop.

    for l in range(L):
        parameters["W" + str(l+1)] = parameters["W" + str(l+1)] - learning_rate * grads["dW" + str(l+1)]
        parameters["b" + str(l+1)] = parameters["b" + str(l+1)] - learning_rate * grads["db" + str(l+1)]
        
    return parameters
parameters, grads = update_parameters_test_case()
parameters = update_parameters(parameters, grads, 0.1)

print ("W1 = "+ str(parameters["W1"]))
print ("b1 = "+ str(parameters["b1"]))
print ("W2 = "+ str(parameters["W2"]))
print ("b2 = "+ str(parameters["b2"]))
]]>
Planar data classification with one hidden layer 2017-09-06T00:00:00+00:00 Kaka Chen http://kakack.github.io/2017/09/Planar Data Classification with One Hidden Layer Planar data classification with one hidden layer

1 - Package

  • numpy Python的基础科学计算库
  • sklearn 提供简单有效的数据挖掘和分析工具
  • matplotlib Python图像绘制库
  • testCases 提供为我们的函数判断正确率的测试例子
  • planar_utils 提供其他一些有用的函数

# Package imports
import numpy as np
import matplotlib.pyplot as plt
from testCases import *
import sklearn
import sklearn.datasets
import sklearn.linear_model
from planar_utils import plot_decision_boundary, sigmoid, load_planar_dataset, load_extra_datasets

%matplotlib inline

np.random.seed(1) # set a seed so that the results are consistent


2 - 数据集

载入一个花瓣状的2-class数据集,坐标轴分别为X和Y。

X, Y = load_planar_dataset()

# Visualize the data:
plt.scatter(X[0, :], X[1, :], c=Y, s=40, cmap=plt.cm.Spectral);

现在数据中有的是:

  • 一个numpy-array (matrix) X,包含了特征值(X1, X2)
  • 一个numpy-array (vector) Y,包含了标签(红色:0;蓝色:1)

更直观地感受一下数据:


shape_X = X.shape
shape_Y = Y.shape
m = shape_Y[1]  # training set size

print ('The shape of X is: ' + str(shape_X))
print ('The shape of Y is: ' + str(shape_Y))
print ('I have m = %d training examples!' % (m))

# The shape of X is: (2, 400)
# The shape of Y is: (1, 400)
# I have m = 400 training examples!


3 - 一个简单的Logistic Regression

sklearn自带的线性logistic regression分类器做简单分类:


# Train the logistic regression classifier
clf = sklearn.linear_model.LogisticRegressionCV();
clf.fit(X.T, Y.T);

# Plot the decision boundary for logistic regression
plot_decision_boundary(lambda x: clf.predict(x), X, Y)
plt.title("Logistic Regression")

# Print accuracy
LR_predictions = clf.predict(X.T)
print ('Accuracy of logistic regression: %d ' % float((np.dot(Y,LR_predictions) + np.dot(1-Y,1-LR_predictions))/float(Y.size)*100) +
       '% ' + "(percentage of correctly labelled datapoints)")
       
# Accuracy of logistic regression: 47 % (percentage of correctly labelled datapoints)

显然这个数据集不是线性可分的


4 - 神经网络模型

以下是我们希望迅雷的神经网络模型

在数学上,对于一个例子:

当有了所有例子上的预测值之后,可以得到损失函数

通常获得一个神经网络模型的方法是:

  • 定义神经网络结构(包括输入个数、隐藏层数等)
  • 初始化模型参数
  • 循环:
    • 执行forward propagation
    • 计算损失cost
    • 执行backward propagation,获得梯度
    • 更新参数(梯度下降)

4.1 定义神经网络结构

  • n_x: 输入层大小
  • n_h: 隐藏层大小
  • n_y: 输出层大小

# GRADED FUNCTION: layer_sizes

def layer_sizes(X, Y):
    """
    Arguments:
    X -- input dataset of shape (input size, number of examples)
    Y -- labels of shape (output size, number of examples)
    
    Returns:
    n_x -- the size of the input layer
    n_h -- the size of the hidden layer
    n_y -- the size of the output layer
    """
    
    n_x = X.shape[0] # size of input layer
    n_h = 4
    n_y = Y.shape[0] # size of output layer
    return (n_x, n_h, n_y)


X_assess, Y_assess = layer_sizes_test_case()
(n_x, n_h, n_y) = layer_sizes(X_assess, Y_assess)
print("The size of the input layer is: n_x = " + str(n_x))
print("The size of the hidden layer is: n_h = " + str(n_h))
print("The size of the output layer is: n_y = " + str(n_y))

# The size of the input layer is: n_x = 5
# The size of the hidden layer is: n_h = 4
# The size of the output layer is: n_y = 2

4.2 初始化参数

  • np.random.randn(a,b) * 0.01来初始化w
  • np.zeros((a,b))来初始化b

# GRADED FUNCTION: initialize_parameters

def initialize_parameters(n_x, n_h, n_y):
    """
    Argument:
    n_x -- size of the input layer
    n_h -- size of the hidden layer
    n_y -- size of the output layer
    
    Returns:
    params -- python dictionary containing your parameters:
                    W1 -- weight matrix of shape (n_h, n_x)
                    b1 -- bias vector of shape (n_h, 1)
                    W2 -- weight matrix of shape (n_y, n_h)
                    b2 -- bias vector of shape (n_y, 1)
    """
    
    np.random.seed(2) 
    
    # we set up a seed so that your output matches ours although the initialization is random.
    
    W1 = np.random.randn(n_h, n_x) * 0.01
    b1 = np.zeros((n_h, 1))
    W2 = np.random.randn(n_y, n_h) * 0.01
    b2 = np.zeros((n_y, 1))
    
    assert (W1.shape == (n_h, n_x))
    assert (b1.shape == (n_h, 1))
    assert (W2.shape == (n_y, n_h))
    assert (b2.shape == (n_y, 1))
    
    parameters = {"W1": W1,
                  "b1": b1,
                  "W2": W2,
                  "b2": b2}
    
    return parameters


n_x, n_h, n_y = initialize_parameters_test_case()

parameters = initialize_parameters(n_x, n_h, n_y)
print("W1 = " + str(parameters["W1"]))
print("b1 = " + str(parameters["b1"]))
print("W2 = " + str(parameters["W2"]))
print("b2 = " + str(parameters["b2"]))


**W1** [[-0.00416758 -0.00056267] [-0.02136196 0.01640271] [-0.01793436 -0.00841747] [ 0.00502881 -0.01245288]]
**b1** [[ 0.] [ 0.] [ 0.] [ 0.]]
**W2** [[-0.01057952 -0.00909008 0.00551454 0.02292208]]
**b2** [[ 0.]]

4.3 循环

# GRADED FUNCTION: forward_propagation

def forward_propagation(X, parameters):
    """
    Argument:
    X -- input data of size (n_x, m)
    parameters -- python dictionary containing your parameters (output of initialization function)
    
    Returns:
    A2 -- The sigmoid output of the second activation
    cache -- a dictionary containing "Z1", "A1", "Z2" and "A2"
    """
    # Retrieve each parameter from the dictionary "parameters"
    W1 = parameters['W1']
    b1 = parameters['b1']
    W2 = parameters['W2']
    b2 = parameters['b2']
    
    # Implement Forward Propagation to calculate A2 (probabilities)
    Z1 = np.dot(W1,X) + b1
    A1 = np.tanh(Z1)
    Z2 = np.dot(W2,A1) + b2
    A2 = sigmoid(Z2)
    
    assert(A2.shape == (1, X.shape[1]))
    
    cache = {"Z1": Z1,
             "A1": A1,
             "Z2": Z2,
             "A2": A2}
    
    return A2, cache


X_assess, parameters = forward_propagation_test_case()

A2, cache = forward_propagation(X_assess, parameters)

# Note: we use the mean here just to make sure that your output matches ours. 
print(np.mean(cache['Z1']) ,np.mean(cache['A1']),np.mean(cache['Z2']),np.mean(cache['A2']))

# -0.000499755777742 -0.000496963353232 0.000438187450959 0.500109546852

既然已经得到了包含所有a[2](i)A2,那么可以计算损失函数:

为了方便起见,可以这样计算:

logprobs = np.multiply(np.log(A2),Y)
cost = - np.sum(logprobs)              
# no need to use a for loop!
# GRADED FUNCTION: compute_cost

def compute_cost(A2, Y, parameters):
    """
    Computes the cross-entropy cost given in equation 
    
    Arguments:
    A2 -- The sigmoid output of the second activation, of shape (1, number of examples)
    Y -- "true" labels vector of shape (1, number of examples)
    parameters -- python dictionary containing your parameters W1, b1, W2 and b2
    
    Returns:
    cost -- cross-entropy cost given equation 
    """
    
    m = Y.shape[1] # number of example

    # Compute the cross-entropy cost

    logprobs = np.multiply(np.log(A2), Y)
    cost = - np.sum(np.multiply(np.log(A2), Y) + np.multiply(np.log(1. - A2), 1. - Y)) / m
    
    cost = np.squeeze(cost)     # makes sure cost is the dimension we expect. 
                                # E.g., turns [[17]] into 17 
    assert(isinstance(cost, float))
    
    return cost
A2, Y_assess, parameters = compute_cost_test_case()

print("cost = " + str(compute_cost(A2, Y_assess, parameters)))

# cost = 0.692919893776

通过在forward propagation中计算得到的cache,可以用在backward propagation的计算当中。

# GRADED FUNCTION: backward_propagation

def backward_propagation(parameters, cache, X, Y):
    """
    Implement the backward propagation using the instructions above.
    
    Arguments:
    parameters -- python dictionary containing our parameters 
    cache -- a dictionary containing "Z1", "A1", "Z2" and "A2".
    X -- input data of shape (2, number of examples)
    Y -- "true" labels vector of shape (1, number of examples)
    
    Returns:
    grads -- python dictionary containing your gradients with respect to different parameters
    """
    m = X.shape[1]
    
    # First, retrieve W1 and W2 from the dictionary "parameters".

    W1 = parameters['W1']
    W2 = parameters['W2']
        
    # Retrieve also A1 and A2 from dictionary "cache".

    A1 = cache['A1']
    A2 = cache['A2']
    
    # Backward propagation: calculate dW1, db1, dW2, db2. 

    dZ2= A2 - Y
    dW2 = np.dot(dZ2, A1.T) / m
    db2 = np.sum(dZ2, axis = 1, keepdims = True) / m
    dZ1 = np.dot(W2.T, dZ2) * (1 - A1**2)
    dW1 = np.dot(dZ1, X.T) / m
    db1 = np.sum(dZ1, axis = 1, keepdims = True) / m
    
    grads = {"dW1": dW1,
             "db1": db1,
             "dW2": dW2,
             "db2": db2}
    
    return grads
parameters, cache, X_assess, Y_assess = backward_propagation_test_case()

grads = backward_propagation(parameters, cache, X_assess, Y_assess)
print ("dW1 = "+ str(grads["dW1"]))
print ("db1 = "+ str(grads["db1"]))
print ("dW2 = "+ str(grads["dW2"]))
print ("db2 = "+ str(grads["db2"]))
**dW1** [[ 0.01018708 -0.00708701] [ 0.00873447 -0.0060768 ] [-0.00530847 0.00369379] [-0.02206365 0.01535126]]
**db1** [[-0.00069728] [-0.00060606] [ 0.000364 ] [ 0.00151207]]
**dW2** [[ 0.00363613 0.03153604 0.01162914 -0.01318316]]
**db2** [[ 0.06589489]]

通过更新规则,使用梯度下降算法,可以利用(dW1, db1, dW2, db2)来更新参数(W1, b1, W2, b2)

计算方法:

其中,学习率α选取的好坏对于模型的质量有很大的影响:

# GRADED FUNCTION: update_parameters

def update_parameters(parameters, grads, learning_rate = 1.2):
    """
    Updates parameters using the gradient descent update rule given above
    
    Arguments:
    parameters -- python dictionary containing your parameters 
    grads -- python dictionary containing your gradients 
    
    Returns:
    parameters -- python dictionary containing your updated parameters 
    """
    # Retrieve each parameter from the dictionary "parameters"

    W1 = parameters['W1']
    b1 = parameters['b1']
    W2 = parameters['W2']
    b2 = parameters['b2']
    
    # Retrieve each gradient from the dictionary "grads"

    dW1 = grads['dW1']
    db1 = grads['db1']
    dW2 = grads['dW2']
    db2 = grads['db2']
    
    # Update rule for each parameter

    W1 = W1 - learning_rate * dW1
    b1 = b1 - learning_rate * db1
    W2 = W2 - learning_rate * dW2
    b2 = b2 - learning_rate * db2
    
    parameters = {"W1": W1,
                  "b1": b1,
                  "W2": W2,
                  "b2": b2}
    
    return parameters

parameters, grads = update_parameters_test_case()
parameters = update_parameters(parameters, grads)

print("W1 = " + str(parameters["W1"]))
print("b1 = " + str(parameters["b1"]))
print("W2 = " + str(parameters["W2"]))
print("b2 = " + str(parameters["b2"]))
**W1** [[-0.00643025 0.01936718] [-0.02410458 0.03978052] [-0.01653973 -0.02096177] [ 0.01046864 -0.05990141]]
**b1** [[ -1.02420756e-06] [ 1.27373948e-05] [ 8.32996807e-07] [ -3.20136836e-06]]
**W2** [[-0.01041081 -0.04463285 0.01758031 0.04747113]]
**b2** [[ 0.00010457]]

4.4 将所有函数汇聚到nn_model()

# GRADED FUNCTION: nn_model

def nn_model(X, Y, n_h, num_iterations = 10000, print_cost=False):
    """
    Arguments:
    X -- dataset of shape (2, number of examples)
    Y -- labels of shape (1, number of examples)
    n_h -- size of the hidden layer
    num_iterations -- Number of iterations in gradient descent loop
    print_cost -- if True, print the cost every 1000 iterations
    
    Returns:
    parameters -- parameters learnt by the model. They can then be used to predict.
    """
    
    np.random.seed(3)
    n_x = layer_sizes(X, Y)[0]
    n_y = layer_sizes(X, Y)[2]
    
    # Initialize parameters, then retrieve W1, b1, W2, b2. Inputs: "n_x, n_h, n_y". Outputs = "W1, b1, W2, b2, parameters".

    parameters = initialize_parameters(n_x, n_h, n_y)
    W1 = parameters['W1']
    b1 = parameters['b1']
    W2 = parameters['W2']
    b2 = parameters['b2']
    
    # Loop (gradient descent)

    for i in range(0, num_iterations):
         
        # Forward propagation. Inputs: "X, parameters". Outputs: "A2, cache".
        A2, cache = forward_propagation(X, parameters)
        
        # Cost function. Inputs: "A2, Y, parameters". Outputs: "cost".
        cost = compute_cost(A2, Y, parameters)
 
        # Backpropagation. Inputs: "parameters, cache, X, Y". Outputs: "grads".
        grads = backward_propagation(parameters, cache, X, Y)
 
        # Gradient descent parameter update. Inputs: "parameters, grads". Outputs: "parameters".
        parameters = update_parameters(parameters, grads)
                
        # Print the cost every 1000 iterations
        if print_cost and i % 1000 == 0:
            print ("Cost after iteration %i: %f" %(i, cost))

    return parameters
X_assess, Y_assess = nn_model_test_case()

parameters = nn_model(X_assess, Y_assess, 4, num_iterations=10000, print_cost=False)
print("W1 = " + str(parameters["W1"]))
print("b1 = " + str(parameters["b1"]))
print("W2 = " + str(parameters["W2"]))
print("b2 = " + str(parameters["b2"]))
**W1** [[-4.18494056 5.33220609] [-7.52989382 1.24306181] [-4.1929459 5.32632331] [ 7.52983719 -1.24309422]]
**b1** [[ 2.32926819] [ 3.79458998] [ 2.33002577] [-3.79468846]]
**W2** [[-6033.83672146 -6008.12980822 -6033.10095287 6008.06637269]]
**b2** [[-52.66607724]]

4.5 预测

预测方法:

# GRADED FUNCTION: predict

def predict(parameters, X):
    """
    Using the learned parameters, predicts a class for each example in X
    
    Arguments:
    parameters -- python dictionary containing your parameters 
    X -- input data of size (n_x, m)
    
    Returns
    predictions -- vector of predictions of our model (red: 0 / blue: 1)
    """
    
    # Computes probabilities using forward propagation, and classifies to 0/1 using 0.5 as the threshold.

    A2, cache = forward_propagation(X, parameters)
    predictions = np.array([0 if i <= 0.5 else 1 for i in np.squeeze(A2)])
    
    return predictions
parameters, X_assess = predict_test_case()

predictions = predict(parameters, X_assess)
print("predictions mean = " + str(np.mean(predictions)))

# predictions mean = 0.666666666667

接下来在一个有h层隐藏层的神经网络中做测试:

# Build a model with a n_h-dimensional hidden layer
parameters = nn_model(X, Y, n_h = 4, num_iterations = 10000, print_cost=True)

# Plot the decision boundary
plot_decision_boundary(lambda x: predict(parameters, x.T), X, Y)
plt.title("Decision Boundary for hidden layer size " + str(4))

# Cost after iteration 0: 0.693048
# Cost after iteration 1000: 0.288083
# Cost after iteration 2000: 0.254385
# Cost after iteration 3000: 0.233864
# Cost after iteration 4000: 0.226792
# Cost after iteration 5000: 0.222644
# Cost after iteration 6000: 0.219731
# Cost after iteration 7000: 0.217504
# Cost after iteration 8000: 0.219471
# Cost after iteration 9000: 0.218612

# Print accuracy
predictions = predict(parameters, X)
print ('Accuracy: %d' % float((np.dot(Y,predictions.T) + np.dot(1-Y,1-predictions.T))/float(Y.size)*100) + '%')

# Accuracy: 90%

4.6 优化隐藏层大小

# This may take about 2 minutes to run

plt.figure(figsize=(16, 32))
hidden_layer_sizes = [1, 2, 3, 4, 5, 20, 50]
for i, n_h in enumerate(hidden_layer_sizes):
    plt.subplot(5, 2, i+1)
    plt.title('Hidden Layer of size %d' % n_h)
    parameters = nn_model(X, Y, n_h, num_iterations = 5000)
    plot_decision_boundary(lambda x: predict(parameters, x.T), X, Y)
    predictions = predict(parameters, X)
    accuracy = float((np.dot(Y,predictions.T) + np.dot(1-Y,1-predictions.T))/float(Y.size)*100)
    print ("Accuracy for {} hidden units: {} %".format(n_h, accuracy))
    
# Accuracy for 1 hidden units: 67.5 %
# Accuracy for 2 hidden units: 67.25 %
# Accuracy for 3 hidden units: 90.75 %
# Accuracy for 4 hidden units: 90.5 %
# Accuracy for 5 hidden units: 91.25 %
# Accuracy for 20 hidden units: 90.0 %
# Accuracy for 50 hidden units: 90.25 %

  • 较大的模型能更好的契合训练集,直到出现过拟合现象
  • 上述例子中最好的隐藏层个数约为5个,既可以有很高的契合度,也不出现过拟合
  • 也可以引入正则化的办法来防止很大的模型过拟合
]]>
Logistic regression with a neural network mindset 2017-09-05T00:00:00+00:00 Kaka Chen http://kakack.github.io/2017/09/Logistic Regression with a Neural Network mindset 通过神经网络mindset实现简单的Logistic Regression

1 - Package

  • numpy Python科学计算的基础包
  • h5py 与存放在H5文件中的数据集进行科学交互的包
  • matplotlib Python绘图包
  • PILscipy 用来在最后测试自定义的图片
import numpy as np
import matplotlib.pyplot as plt
import h5py
import scipy
from PIL import Image
from scipy import ndimage
from lr_utils import load_dataset

%matplotlib inline

2 - 问题数据集概述

问题陈述:给定衣柜数据集(“data.h5”)包含:

  • 一个包含了m_train图片的训练集,分别被标注了是猫(y=1)或者不是猫(y=0)
  • 一个包含了m_test图片的测试集,也分别被标注了是猫或者不是猫
  • 每个图片的shape都是(num_px, num_px, 3),其中3表示图像有3个频道(RGB),因此每个图像都是一个正方形(height = num_px, width = num_px
# Loading the data (cat/non-cat)

train_set_x_orig, train_set_y, test_set_x_orig, test_set_y, classes = load_dataset()

载入图片,在变量后加后缀_orig表示这是原数据,仍需要进一步处理,其中train_set_x_origtrain_set_x_orig每一行都代表了一张图片,可以通过下面的例子查看当前的图片:

# Example of a picture
index = 25
plt.imshow(train_set_x_orig[index])
print ("y = " + str(train_set_y[:, index]) + ", it's a '" + classes[np.squeeze(train_set_y[:, index])].decode("utf-8") +  "' picture.")

定义变量:

  • m_train:训练集的个数
  • m_test:测试集的个数
  • num_px:训练图像的长和宽
m_train = train_set_x_orig.shape[0]
m_test = test_set_x_orig.shape[0]
num_px = train_set_x_orig.shape[1]

print ("Number of training examples: m_train = " + str(m_train))
print ("Number of testing examples: m_test = " + str(m_test))
print ("Height/Width of each image: num_px = " + str(num_px))
print ("Each image is of size: (" + str(num_px) + ", " + str(num_px) + ", 3)")
print ("train_set_x shape: " + str(train_set_x_orig.shape))
print ("train_set_y shape: " + str(train_set_y.shape))
print ("test_set_x shape: " + str(test_set_x_orig.shape))
print ("test_set_y shape: " + str(test_set_y.shape))

# Number of training examples: m_train = 209
# Number of testing examples: m_test = 50
# Height/Width of each image: num_px = 64
# Each image is of size: (64, 64, 3)
# train_set_x shape: (209, 64, 64, 3)
# train_set_y shape: (1, 209)
# test_set_x shape: (50, 64, 64, 3)
# test_set_y shape: (1, 50)

为了方便起见,将图片reshape到一个新的格式:(num_pxnum_px3, 1),这样之后我们的训练集和测试集都是一个扁平化的numpy-array,每一列都代表了一个图像,其中总共m_train(或m_test)列。

# Reshape the training and test examples

train_set_x_flatten = train_set_x_orig.reshape(train_set_x_orig.shape[0], -1).T
test_set_x_flatten = test_set_x_orig.reshape(test_set_x_orig.shape[0], -1).T

print ("train_set_x_flatten shape: " + str(train_set_x_flatten.shape))
print ("train_set_y shape: " + str(train_set_y.shape))
print ("test_set_x_flatten shape: " + str(test_set_x_flatten.shape))
print ("test_set_y shape: " + str(test_set_y.shape))
print ("sanity check after reshaping: " + str(train_set_x_flatten[0:5,0]))

# train_set_x_flatten shape: (12288, 209)
# train_set_y shape: (1, 209)
# test_set_x_flatten shape: (12288, 50)
# test_set_y shape: (1, 50)
# sanity check after reshaping: [17 31 56 22 33]

为了表示一个彩色图片,,每一个像素都是一个三个值组成的向量,每个值都位于区间[0, 255]。一种常见的机器学习预处理手段是将数据集中心化和标准化,这意味着在每个例子上都减去整个numpy array的平均值,然后每个例子都除以整个numpy array的标准偏差,而对于图片数据集,就可以简单地只是将每一行除以255(一个像素通道的最大值)。

# Standardize our dataset.
train_set_x = train_set_x_flatten/255.
test_set_x = test_set_x_flatten/255.

一般的预处理步骤:

  • 找出问题数据的维度和shape(m_train, m_test, num_px, …)
  • 对数据集做reshape,使得其向量化(num_px * num_px * 3, 1)
  • 标准化数据

3 - 学习算法基本架构

数学表达式:

对于一个例子

其中损失函数是所有训练例子的总和:


4 - 建立算法的各个部分

建立神经网络的主要步骤:

  1. 定义模型类型和结构(例如输入的特征数量等)
  2. 初始化模型参数
  3. 循环:
    • 计算当前的损失(forward propagation)
    • 计算当前的梯度(backward propagation)
    • 更新参数(gradient descent)

4.1 Helper Function

计算

# GRADED FUNCTION: sigmoid

def sigmoid(z):
    """
    Compute the sigmoid of z

    Arguments:
    z -- A scalar or numpy array of any size.

    Return:
    s -- sigmoid(z)
    """

    s = 1./(1.+np.exp(-z))
    
    return s

4.2 初始化参数:

# GRADED FUNCTION: initialize_with_zeros

def initialize_with_zeros(dim):
    """
    This function creates a vector of zeros of shape (dim, 1) for w and initializes b to 0.
    
    Argument:
    dim -- size of the w vector we want (or number of parameters in this case)
    
    Returns:
    w -- initialized vector of shape (dim, 1)
    b -- initialized scalar (corresponds to the bias)
    """
    
    w = np.zeros((dim, 1))
    b = 0

    assert(w.shape == (dim, 1))
    assert(isinstance(b, float) or isinstance(b, int))
    
    return w, b

4.3 Forward & Backward propagation

Forward Propagation:

  • 得到X
  • 计算
  • 计算损失函数

其中:

# GRADED FUNCTION: propagate

def propagate(w, b, X, Y):
    """
    Implement the cost function and its gradient for the propagation explained above

    Arguments:
    w -- weights, a numpy array of size (num_px * num_px * 3, 1)
    b -- bias, a scalar
    X -- data of size (num_px * num_px * 3, number of examples)
    Y -- true "label" vector (containing 0 if non-cat, 1 if cat) of size (1, number of examples)

    Return:
    cost -- negative log-likelihood cost for logistic regression
    dw -- gradient of the loss with respect to w, thus same shape as w
    db -- gradient of the loss with respect to b, thus same shape as b
    
    Tips:
    - Write your code step by step for the propagation. np.log(), np.dot()
    """
    
    m = X.shape[1]
    
    # FORWARD PROPAGATION (FROM X TO COST)
    A = sigmoid(np.dot(w.T, X) + b)                                      # compute activation
    cost = np.sum(Y * np.log(A) + (1 - Y) * np.log(1 - A))/(-m)                                # compute cost
    
    # BACKWARD PROPAGATION (TO FIND GRAD)
    dw = np.dot(X, (A - Y).T)/m
    db = np.sum(A - Y)/m

    assert(dw.shape == w.shape)
    assert(db.dtype == float)
    cost = np.squeeze(cost)
    assert(cost.shape == ())
    
    grads = {"dw": dw,
             "db": db}
    
    return grads, cost

4.4 优化

更新各个参数来实现梯度下降


# GRADED FUNCTION: optimize

def optimize(w, b, X, Y, num_iterations, learning_rate, print_cost = False):
    """
    This function optimizes w and b by running a gradient descent algorithm
    
    Arguments:
    w -- weights, a numpy array of size (num_px * num_px * 3, 1)
    b -- bias, a scalar
    X -- data of shape (num_px * num_px * 3, number of examples)
    Y -- true "label" vector (containing 0 if non-cat, 1 if cat), of shape (1, number of examples)
    num_iterations -- number of iterations of the optimization loop
    learning_rate -- learning rate of the gradient descent update rule
    print_cost -- True to print the loss every 100 steps
    
    Returns:
    params -- dictionary containing the weights w and bias b
    grads -- dictionary containing the gradients of the weights and bias with respect to the cost function
    costs -- list of all the costs computed during the optimization, this will be used to plot the learning curve.
    
    """
    
    costs = []
    
    for i in range(num_iterations):
        
        
        # Cost and gradient calculation 
        grads, cost = propagate(w, b, X, Y)
        
        # Retrieve derivatives from grads
        dw = grads["dw"]
        db = grads["db"]
        
        # update rule 
        w = w - learning_rate * dw
        b = b - learning_rate * db
        
        # Record the costs
        if i % 100 == 0:
            costs.append(cost)
        
        # Print the cost every 100 training examples
        if print_cost and i % 100 == 0:
            print ("Cost after iteration %i: %f" %(i, cost))
    
    params = {"w": w,
              "b": b}
    
    grads = {"dw": dw,
             "db": db}
    
    return params, grads, costs

预测函数,分两步进行预测:

  • 计算
  • 将结果转化为0(如果activation<=0.5)或者1(如果activation>0.5)
# GRADED FUNCTION: predict

def predict(w, b, X):
    '''
    Predict whether the label is 0 or 1 using learned logistic regression parameters (w, b)
    
    Arguments:
    w -- weights, a numpy array of size (num_px * num_px * 3, 1)
    b -- bias, a scalar
    X -- data of size (num_px * num_px * 3, number of examples)
    
    Returns:
    Y_prediction -- a numpy array (vector) containing all predictions (0/1) for the examples in X
    '''
    
    m = X.shape[1]
    Y_prediction = np.zeros((1,m))
    w = w.reshape(X.shape[0], 1)
    
    # Compute vector "A" predicting the probabilities of a cat being present in the picture
    A = np.dot(w.T, X)
    
    for i in range(A.shape[1]):
        
        # Convert probabilities A[0,i] to actual predictions p[0,i]
        if (A[0, i] > 0.5):
            Y_prediction[0][i] = 1
        else:
            Y_prediction[0][i] = 0
    
    assert(Y_prediction.shape == (1, m))
    
    return Y_prediction



5 - 将算法的各个部分汇总

  • Y_prediction作为测试集的预测结果
  • Y_prediction_train作为训练集的预测结果
  • w, costs, grads作为optimize()的输出

# GRADED FUNCTION: model

def model(X_train, Y_train, X_test, Y_test, num_iterations = 2000, learning_rate = 0.5, print_cost = False):
    """
    Builds the logistic regression model by calling the function you've implemented previously
    
    Arguments:
    X_train -- training set represented by a numpy array of shape (num_px * num_px * 3, m_train)
    Y_train -- training labels represented by a numpy array (vector) of shape (1, m_train)
    X_test -- test set represented by a numpy array of shape (num_px * num_px * 3, m_test)
    Y_test -- test labels represented by a numpy array (vector) of shape (1, m_test)
    num_iterations -- hyperparameter representing the number of iterations to optimize the parameters
    learning_rate -- hyperparameter representing the learning rate used in the update rule of optimize()
    print_cost -- Set to true to print the cost every 100 iterations
    
    Returns:
    d -- dictionary containing information about the model.
    """
        
    # initialize parameters with zeros (≈ 1 line of code)
    w, b = initialize_with_zeros(X_train.shape[0])

    # Gradient descent (≈ 1 line of code)
    parameters, grads, costs = optimize(w, b, X_train, Y_train, num_iterations, learning_rate, print_cost = False)
    
    # Retrieve parameters w and b from dictionary "parameters"
    w = parameters["w"]
    b = parameters["b"]
    
    # Predict test/train set examples (≈ 2 lines of code)
    Y_prediction_test = predict(w, b, X_test)
    Y_prediction_train = predict(w, b, X_train)


    # Print train/test Errors
    print("train accuracy: {} %".format(100 - np.mean(np.abs(Y_prediction_train - Y_train)) * 100))
    print("test accuracy: {} %".format(100 - np.mean(np.abs(Y_prediction_test - Y_test)) * 100))

    
    d = {"costs": costs,
         "Y_prediction_test": Y_prediction_test, 
         "Y_prediction_train" : Y_prediction_train, 
         "w" : w, 
         "b" : b,
         "learning_rate" : learning_rate,
         "num_iterations": num_iterations}
    
    return d

查看结果:

d = model(train_set_x, train_set_y, test_set_x, test_set_y, num_iterations = 2000, learning_rate = 0.005, print_cost = True)

# train accuracy: 98.08612440191388 %
# test accuracy: 70.0 %

这个模型在训练集上表现很好,但是在测试集上表现一般,所以很显然存在过拟合的问题,之后可以通过正则化等方法来解决过拟合问题。


6 - 后续分析

通过控制学习率Learning Rate的大小来查看学习曲线的变化情况


learning_rates = [0.01, 0.001, 0.0001]
models = {}
for i in learning_rates:
    print ("learning rate is: " + str(i))
    models[str(i)] = model(train_set_x, train_set_y, test_set_x, test_set_y, num_iterations = 1500, learning_rate = i, print_cost = False)
    print ('\n' + "-------------------------------------------------------" + '\n')

for i in learning_rates:
    plt.plot(np.squeeze(models[str(i)]["costs"]), label= str(models[str(i)]["learning_rate"]))

plt.ylabel('cost')
plt.xlabel('iterations')

legend = plt.legend(loc='upper center', shadow=True)
frame = legend.get_frame()
frame.set_facecolor('0.90')
plt.show()

  • 不同的学习率能得到不同的损失值,因此也会有不同的预测结果
  • 如果学习率太大,损失值会过于陡峭地上下震荡,甚至会发生分歧
  • 一个很低的学习率并不代表一定会得到一个很好的模型,因为需要看看是否出现过拟合了,这在训练集预测结果大大好于测试集预测结果时发生
  • 在深度学习中,我们常常建议:
    • 选择能最小化损失函数的学习率
    • 如果模型过拟合了,使用其他手段来解决
]]>
基于haar Like特征的人脸识别 2017-08-14T00:00:00+00:00 Kaka Chen http://kakack.github.io/2017/08/基于Haar-Like特征的人脸识别
  • 使用Haar-like特征做检测。
  • 使用积分图(Integral Image)对Haar-like特征求值进行加速。
  • 使用AdaBoost算法训练区分人脸和非人脸的强分类器。
  • 使用筛选式级联把强分类器级联到一起,提高准确率。
  • 简单说来,PAC学习模型不要求你每次都正确,只要能在多项式个样本和多项式时间内得到满足需求的正确率,就算是一个成功的学习。“学习”是模式明显清晰或模式不存在时仍能获取知识的一种“过程”,并给出了一个从计算角度来获得这种“过程”的方法,这种方法包括:

    1. 适当信息收集机制的选择;
    2. 学习的协定;
    3. 对能在合理步骤内完成学习的概念的分类。

    强学习和弱学习:

    • 弱学习:一个学习算法对一组概念的识别率只比随机识别好一点
    • 强学习:一个学习算法对一组概率的识别率很高

    人脸识别的应用步骤:

    • 人脸检测(Face Detection)。检测到人脸所在的区域。并进行一系列的矫正。
    • 人脸校准(Face Alignment)。人脸校准指的是在图片中寻找到鼻子、眼睛、嘴巴之类的位置。
    • 信息识别(Info Recognition)。进行性别、年龄等信息的分析和识别。

    基于知识的人脸检测方法

    • 模板匹配
    • 人脸特征
    • 形状与边缘
    • 纹理特性
    • 颜色特征

    基于统计的人脸检测方法

    • 主成分分析与特征脸
    • 神经网络方法
    • 支持向量机
    • 隐马尔可夫模型
    • AdaBoost算法

    人脸检测Viola-Jones算法

    • Haar-like特征
    • AdaBoost分类器
    • Cascade级联分类器
    Haar-like特征:

    一个矩形哈尔特征可以定义为矩形中几个区域的像素和的差值,可以具有任意的位置和尺寸。这种特质也被称为2矩形特征(2-rectangle feature)。 维奥拉和琼斯也定义了3矩形特征和4矩形特征。这个值表明了图像的特定区域的某些特性。每一个特征可以描述图像上特定特性的存在或不存在,比如边缘或者纹理的变化。

    定义该模板的特征值为模板矩阵内白色矩形像素和减去黑色矩形像素和。

    haar-like feature = sum(white) - sum(black)
    # 上图A、B、D
    haar-like feature = sum(white) - 2*sum(black)
    # 上图C,为了使得两种矩形区域内像素数目一致
    

    其他一些特征模板

    矩阵特征值根据特征模板的大小、位置和模板类型的不同而不同,因此是关于这三个因素的函数。

    同时定义了积分图(Integral Image),是一张与原图像大小完全相同的图片,每个点记录了该点对应位置左上方所有像素的和。

    利用积分图可以只查询4次就求得一个2矩阵特征的值:

    如图所示白色部分计算结果即B(5)+B(1)-B(2)-B(6),黑色部分计算结果即B(4)+B(2)-B(3)-B(5),二者的差即所示矩阵的特征值。

    AdaBoost算法

    AdaBoost的核心思想就是从多个弱分类器中结合,生成一个强分类器。数学书表达就是:

    其中F是强分类器,f是各个弱分类器,x是特征向量,α是各个弱分类器对应的权重,N是弱分类器的个数。

    其中弱分类器定义为:

    其中表示一个输入窗口x,通过这个函数提取特征值,然后通过一个阈值θ判定是否是目标物体。是为了控制不等式左右的符号。

    假设样本图像为,共m个,初始化权重

    For t=1, 2, 3…T:

    1. 归一化权重
    2. 对于每个特征,训练并记录一个分类器,记为,每个分类器都只使用一个特征进行训练,而对于该特征的误差可以衡量:
    3. 选择拥有最低误差的分类器记为
    4. 更新权重,如果分类正确,错误

    End for

    最终得到强分类器:

    其中

    Cascade级联分类器

    就是讲几个通过AdaBoost方法得到的强分类器进行排序,简单的在左复杂的在右,因为认为一般照片中人脸所占部分较小,可以较为大胆地去除很大一部分非人脸的内容


    OpenCV-Python中的例子

    import cv2
    
    imagepath ='./catface2.jpg'
    # 设定待识别的图像在文件夹中的位置
    
    image = cv2.imread(imagepath)
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    # 图片读入,转化为灰度图
    
    face_cascade = cv2.CascadeClassifier(r'./haarcascade_frontalcatface_extended.xml')
    # haarcascade_frontalface_default.xml是OpenCV自带的根据haar-like特征值已经训练好的模型,可以在opencv文件夹下./share/OpenCV/haarcascades文件夹中找到,还有其他不同的多种识别模型
    
    faces = face_cascade.detectMultiScale(
        gray,
        scaleFactor = 1.15,
        minNeighbors = 5,
        minSize = (5,5),
        flags = cv2.cv.CV_HAAR_SCALE_IMAGE
    )
    
    print "Found {0} faces!".format(len(faces))
    # 打印出识别信息
    
    for(x,y,w,h) in faces:
        cv2.rectangle(image,(x,y),(x+w,y+w),(0,255,0),2)
    # 根据识别结果画长方形显示面部
    
    cv2.imshow("Find Faces!",image)
    cv2.waitKey(0)
    
    ]]>
    Tensorflow解决xor问题 2017-06-18T00:00:00+00:00 Kaka Chen http://kakack.github.io/2017/06/TensorFlow解决XOR问题 Introduce

    TensorFlow是用于数值计算的开源软件库,是其他机器学习框架中最小的。 它最初由Google Brain Team的研究人员和工程师开发,目的是鼓励对深层架构的研究。 然而,TensorFlow环境提供了一套适用于数字编程领域的大量工具。 计算是在数据流图的概念下进行的。 图中的节点表示数学运算,而图形边缘则代表张量(多维数据阵列)。 软件包的核心是用C ++编写,但提供了一个很好的文档Python API。 主要特征是其象征性的方法,其允许对正向模型的一般定义,使得相应衍生物的计算完全与环境本身相关。

    The Data Flow Graph: 为了利用多核CPU,GPU甚至GPU集群的并行计算能力,数值计算的动态被认为是有向图,其中每个节点表示数学运算,边界描述了输入/输出关系节点。

    Tensor: 一个流过Data Flow Graph的n维数组类型

    Variable:用于表示参数的符号对象。 他们在符号级别被利用来计算 衍生结果和中间变量,但通常必须在Session中显式初始化。

    Optimizer:它是提供从损失函数计算梯度的方法并通过所有变量应用反向传播的组件。 TensorFlow中提供了一个集合来实现经典优化算法。

    Session:一个Graph必须在Session中启动,它将graph安置到CPU或者GPU上并为其运行计算提供方法。


    用TensorFlow解决XOR问题

    导入TensorFlow包,创建用于训练的X和Y值

    import tensorflow as tf
    
    X = [[0, 0], [0, 1], [1, 0], [1, 1]]
    Y = [[0], [1], [1], [0]]
    

    设置占位符

    x_ = tf.placeholder(tf.float32, shape=[4, 2])
    y_ = tf.placeholder(tf.float32, shape=[4, 1])
    
    HU = 3
    

    初始化权重向量,一开始以随机值为值。其中tf.nn.sigmoid为一种激活函数,y = 1 / (1 + exp(-x)), 用处是与其它层的输出联合使用可生成特征图,用于对某些运算的结果进行平滑或微分,如tf.nn.relu。激活函数是用来加入非线性因素的,因为线性模型的表达能力不够。

    W1 = tf.Variable(tf.random_uniform([2, HU], -1.0, 1.0))
    b1 = tf.Variable(tf.zeros([HU]))
    O = tf.nn.sigmoid(tf.matmul(x_, W1) + b1)
    
    W2 = tf.Variable(tf.random_uniform([HU, 1], -1.0, 1.0))
    b2 = tf.Variable(tf.zeros([1]))
    y = tf.nn.sigmoid(tf.matmul(O, W2) + b2)
    
    # 2-layer ann
    
    

    计算损失值,训练就使用梯度下降的办法GradientDescentOptimizer

    cost = tf.reduce_sum(tf.square(y_ - y), reduction_indices=[0])
    train_step = tf.train.GradientDescentOptimizer(0.1).minimize(cost)
    

    设置tf运行的Session,并将占位符初始化

    sess = tf.Session()
    sess.run(tf.global_variables_initializer())
    

    设置运行运行步长

    Ecoches = 5000
    for i in range(Ecoches):
        sess.run(train_step, feed_dict={x_ : X, y_ :Y})
        if i%500 == 0:
            print ('Epoch ', i)
            print ('Cost ', sess.run(cost, feed_dict={x_: X, y_: Y}))
    
    

    计算预测值与实际值之间的准确率

    correcct_prediction = abs(y_ - y) < 0.5
    cast = tf.cast(correcct_prediction, "float")
    accuracy = tf.reduce_mean(cast)
    

    输出结果

    yy, aa = sess.run([y, accuracy], feed_dict={x_: X, y_: Y})
    
    print "Output: ", yy
    print "Accuracy: ", aa
    
    
    Output:  [[ 0.10211191]
     [ 0.91052949]
     [ 0.88228792]
     [ 0.08940154]]
    Accuracy:  1.0
    
    
    ]]>
    Python numpy 2017-06-15T00:00:00+00:00 Kaka Chen http://kakack.github.io/2017/06/Python Numpy Numpy Module

    Numpy的核心对象是均匀的齐次多维数组

    Numpy的维度dimension叫做axes,其中axes的数量叫做rank

    称为ndarray,也可以叫做array,但是与标准Python库中的array不同。

    ndarray的属性有以下:

    • ndarray.ndim:array的维度axes的数量,即rank
    • ndarray.shape:array的维度,返回一个tuple,表示array中每个维度的大小,比如一个n行m列的矩阵,shape值为(n, m) ,因此shape值的长度就是rank,也就是ndarray.ndim
    • ndarray.size:array中所有元素的个数,等于shape中各个值的乘积
    • ndarray.dtype:array中元素的类型
    • ndarray.itemsize:array中每个元素占的byte数
    • ndarray.data:保存着array真实元素的buffer,通常不需要

    Array创建

    可以通过一个通常的Python list或者tuple创建,如:

    >>> a = np.array([1,2,3])
    >>> a
    array([1, 2, 3])
    

    注,需要以list作为单个参数,传入,而不是传入若干个数字参数:a = np.array([1,2,3]) # wrong

    也可以用zeros或者ones创建全是0或者全是1的array:

    
    >>> np.zeros( (3,4) )
    array([[ 0.,  0.,  0.,  0.],
           [ 0.,  0.,  0.,  0.],
           [ 0.,  0.,  0.,  0.]])
    >>> np.ones( (2,3,4), dtype=np.int16 )                # dtype can also be specified
    array([[[ 1, 1, 1, 1],
            [ 1, 1, 1, 1],
            [ 1, 1, 1, 1]],
           [[ 1, 1, 1, 1],
            [ 1, 1, 1, 1],
            [ 1, 1, 1, 1]]], dtype=int16)
    >>> np.empty( (2,3) )                                 # uninitialized, output may vary
    array([[  3.73603959e-262,   6.02658058e-154,   6.55490914e-260],
           [  5.30498948e-313,   3.14673309e-307,   1.00000000e+000]])
    

    或者通过arange函数创建序列的array

    >>> a = np.arange(6)                         # 1d array
    >>> print(a)
    [0 1 2 3 4 5]
    >>>
    >>> b = np.arange(12).reshape(4,3)           # 2d array
    >>> print(b)
    [[ 0  1  2]
     [ 3  4  5]
     [ 6  7  8]
     [ 9 10 11]]
    >>>
    >>> c = np.arange(24).reshape(2,3,4)         # 3d array
    >>> print(c)
    [[[ 0  1  2  3]
      [ 4  5  6  7]
      [ 8  9 10 11]]
     [[12 13 14 15]
      [16 17 18 19]
      [20 21 22 23]]]
    

    基本操作

    >>> a = np.array( [20,30,40,50] )
    >>> b = np.arange( 4 )
    >>> b
    array([0, 1, 2, 3])
    >>> c = a-b
    >>> c
    array([20, 29, 38, 47])
    >>> b**2
    array([0, 1, 4, 9])
    >>> 10*np.sin(a)
    array([ 9.12945251, -9.88031624,  7.4511316 , -2.62374854])
    >>> a<35
    array([ True, True, False, False], dtype=bool)
    
    

    其中+=*=操作都是在原有基础上修改array对象,而不是创建一个新的对象


    通用函数

    提供sin, cos, exp等数学函数,称为universal functions(ufunc)

    >>> B = np.arange(3)
    >>> B
    array([0, 1, 2])
    >>> np.exp(B)
    array([ 1.        ,  2.71828183,  7.3890561 ])
    >>> np.sqrt(B)
    array([ 0.        ,  1.        ,  1.41421356])
    >>> C = np.array([2., -1., 4.])
    >>> np.add(B, C)
    array([ 2.,  0.,  6.])
    

    索引、分片和迭代

    对于一维的array来说,可以被索引、分片和迭代,就像普通的Python list

    >>> a = np.arange(10)**3
    >>> a
    array([  0,   1,   8,  27,  64, 125, 216, 343, 512, 729])
    >>> a[2]
    8
    >>> a[2:5]
    array([ 8, 27, 64])
    >>> a[:6:2] = -1000    # equivalent to a[0:6:2] = -1000; from start to position 6, exclusive, set every 2nd element to -1000
    >>> a
    array([-1000,     1, -1000,    27, -1000,   125,   216,   343,   512,   729])
    >>> a[ : :-1]                                 # reversed a
    array([  729,   512,   343,   216,   125, -1000,    27, -1000,     1, -1000])
    >>> for i in a:
    ...     print(i**(1/3.))
    ...
    nan
    1.0
    nan
    3.0
    nan
    5.0
    6.0
    7.0
    8.0
    9.0
    

    修改array的shape

    假设有一个(3,4)的array,如何修改其shape,有如下办法:

    >>> a
    array([[ 2.,  8.,  0.,  6.],
           [ 4.,  5.,  1.,  1.],
           [ 8.,  9.,  3.,  6.]])
    >>> a.shape
    (3, 4)
    
    # 扁平化 
    
    >>> a.ravel()  # returns the array, flattened
    array([ 2.,  8.,  0.,  6.,  4.,  5.,  1.,  1.,  8.,  9.,  3.,  6.])
    
    # reshape()命令
    
    >>> a.reshape(6,2)  # returns the array with a modified shape
    array([[ 2.,  8.],
           [ 0.,  6.],
           [ 4.,  5.],
           [ 1.,  1.],
           [ 8.,  9.],
           [ 3.,  6.]])
    
    >>> a.T  # returns the array, transposed
    array([[ 2.,  4.,  8.],
           [ 8.,  5.,  9.],
           [ 0.,  1.,  3.],
           [ 6.,  1.,  6.]])
    >>> a.T.shape
    (4, 3)
    >>> a.shape
    (3, 4)
    

    将不同的Array粘到一起

    不同维度的array可以被粘在一起:vstack()命令

    >>> c
    array([False, False,  True,  True], dtype=bool)
    >>> d
    array([False,  True,  True,  True], dtype=bool)
    >>> np.vstack((c,d))
    array([[False, False,  True,  True],
           [False,  True,  True,  True]], dtype=bool)
    >>> e = np.vstack((c,d))
    >>> e.ravel()
    array([False, False,  True,  True, False,  True,  True,  True], dtype=bool)
    # 这个操作可以用到cifar10的eval里
    

    将一个Array拆分成若干个小arrays

    可以指定维度来拆分成特定个数的arrays

    >>> a = np.floor(10*np.random.random((2,12)))
    >>> a
    array([[ 6.,  1.,  0.,  1.,  0.,  6.,  4.,  0.,  7.,  6.,  6.,  2.],
           [ 9.,  1.,  8.,  5.,  0.,  1.,  6.,  1.,  3.,  4.,  4.,  8.]])
    # 生成一个(2,12)的array
    >>> np.hsplit(a, 3)
    [array([[ 6.,  1.,  0.,  1.],
           [ 9.,  1.,  8.,  5.]]), array([[ 0.,  6.,  4.,  0.],
           [ 0.,  1.,  6.,  1.]]), array([[ 7.,  6.,  6.,  2.],
           [ 3.,  4.,  4.,  8.]])]
    # 生成三个(2, 4)的array
    >>> np.hsplit(a, (3, 4))
    [array([[ 6.,  1.,  0.],
           [ 9.,  1.,  8.]]), array([[ 1.],
           [ 5.]]), array([[ 0.,  6.,  4.,  0.,  7.,  6.,  6.,  2.],
           [ 0.,  1.,  6.,  1.,  3.,  4.,  4.,  8.]])]
    

    Copy和Views

    当使用和操作时,有时会将array复制到一个新的array里,但有时仅仅是做了引用:

    # 这里b和a是同一个对象的不同名称而已
    
    >>> a = np.arange(12)
    >>> b = a            # no new object is created
    >>> b is a           # a and b are two names for the same ndarray object
    True
    >>> b.shape = 3,4    # changes the shape of a
    >>> a.shape
    (3, 4)
    

    View方法创造一个新的对象,其中数据一致

    >>> c = a.view()
    >>> c is a
    False
    >>> c.base is a                        # c is a view of the data owned by a
    True
    >>> c.flags.owndata
    False
    >>>
    >>> c.shape = 2,6                      # a's shape doesn't change
    >>> a.shape
    (3, 4)
    >>> c[0,4] = 1234                      # a's data changes
    >>> a
    array([[   0,    1,    2,    3],
           [1234,    5,    6,    7],
           [   8,    9,   10,   11]])
    

    将一个array做切片操作

    >>> s = a[ : , 1:3]     # spaces added for clarity; could also be written "s = a[:,1:3]"
    >>> s[:] = 10           # s[:] is a view of s. Note the difference between s=10 and s[:]=10
    >>> a
    array([[   0,   10,   10,    3],
           [1234,   10,   10,    7],
           [   8,   10,   10,   11]])
    

    Deep Copy

    >>> d = a.copy()                          # a new array object with new data is created
    >>> d is a
    False
    >>> d.base is a                           # d doesn't share anything with a
    False
    >>> d[0,0] = 9999
    >>> a
    array([[   0,   10,   10,    3],
           [1234,   10,   10,    7],
           [   8,   10,   10,   11]])
    
    ]]>
    太平洋的风——记台湾行 2017-05-04T00:00:00+00:00 Kaka Chen http://kakack.github.io/2017/05/太平洋的风——记台湾行 前言

    从台湾回来已经一个月了,终于有时间和兴趣开始写一写那一个礼拜旅行的故事,我一直觉得我是一个很好的旅行策划师,却不是一个很好的游记写手,因为很多感动和体会并不是我原先计划着的,而是各种旅行路上的邂逅和偶遇。

    很早以前就拜读过廖信忠先生写的很多关于台湾的奇闻异事,也听很多朋友聊起过他们所到过的台湾,但总归给我一种盲人摸象的感觉。因此记录一些我在台湾的关键词,谨以纪念这段难忘的旅程。


    从杭州到台湾

    这段旅程打开始就让我觉得不一般,我这辈子第一次买错了飞机票。傻乎乎的我站在出发航站楼里听东航小哥告诉我“您的机票是昨天”的时候,似乎一点都不吃惊,仿佛已经预感到这种坑爹事会降临一样。我熟练地在柜台买了一张最近的直飞台北的机票,出了一笔大血,但也很幸运地坐上了著名的Hello Kitty航班。

    飞机上的便当味道不错,有台湾当地的报纸看,杭州到台北约一小时十五分钟的行程过的很快,感觉一转眼就穿越了这湾被誉为国之殇的海峡,踏上了台湾的土地。

    我一直说很遗憾我们生活的世界里没有时光机,我无法回到过去去亲眼看看汉唐宋元明清的故事,但是很激动,我至少能回到中华民国看看。

    台北桃园机场非常巨大,是许多游客来台湾的第一站,我在这买了悠游卡、拿了随行Wifi、取了现金、盖了入关章,开始了台湾之行。


    台铁和捷运

    台湾的有轨交通非常发达,几乎每个我所到的城市都通了高铁,几个主要城市内也都有很发达的捷运网,出行非常方便。台湾的城际铁路分为台铁和高铁,类似于我们的动车和高铁,我从台北往返高雄坐的就是这个,约2小时不到的车程。而台湾把地铁称为捷运,在台北和高雄两大城市里,几乎遍布了捷运车站,尤其是台北更是密集,印象中相邻最近的两个站不超过400米,直达全市所有值得去的旅游景点。

    台湾的城市带主要集中于岛内西侧,而台北北投到高雄左营的高铁线路无疑是最繁忙的。在高铁上我选了一个邻窗的位子,看着外面一个个熟悉的地名一划而过:桃园、板桥、新竹、台中、彰化、嘉义、台南、高雄。

    其实我并没有在高雄市多做停留,因为一来是不想让太多行程中出现赶路的桥段,二来是高雄作为一个港口工业城市,虽然有自己独特的味道,但似乎并不是我的菜。所以我只是趁着换乘的时候,从左营坐捷运去了趟著名的高雄美丽岛捷运站,近距离看了看著名的光之穹顶


    垦丁

    其实整个旅行从一开始的环岛游到最后的南北游,被我精简地只剩下了两个地方:台北和垦丁。我放弃了曾经教科书中吹的最凶的台湾第一景点日月潭和阿里山,也放弃了很多好朋友吐血推荐的最美东海岸花莲,只为了能在垦丁多呆上几天,静静地看着海发呆,然后去潜个水。事实上,垦丁也确实没有让我失望。

    垦丁位于台湾屏东县恒春镇,在当地人眼中,在没有那么多游客涌入之前,这就是一个没什么人烟的小渔村,以至于当时的民国政府还把一个核电站选址在了这,只要一抬头就能看到矗立在海滩边的两个核电站圆形穹顶,我猜这也是全世界唯一一座位于旅游热区的核电站了吧。

    垦丁的海是那种很温柔的海,风不大,浪也不大,沙滩非常舒服。和其他著名的海滩相比,垦丁很有趣的一点是,蓝色的大海之上,是绿色的远山。我们步行上去过雾社公园,虽然没看到大海也没看到梅花鹿,但是确实是一个很安静的公园,只有零星几个当地人在那边散步,几乎没人说话,就是坐在草坪上发呆。

    猫鼻头公园是我觉得垦丁看海最美的地方,视野非常开阔,一侧是巴士海峡,一侧是台湾海峡,另一侧是太平洋。蓝色的海面翻起白色的浪花打在黑色的岩石上。

    鹅銮鼻公园是垦丁的地标,白色的灯塔是垦丁整个地方的缩影。

    我很喜欢的电影《海角七号》的故事就发生在恒春镇,我们骑着当地随处可见的电车一路驰骋来到了恒春镇上。“海角七号”四个字的白色墙就在阿嘉的家边上。

    垦丁给我另一个印象就是,海上活动真便宜,几乎500块人民币就能潜水了,很遗憾没能玩到其他游戏,不过也算不枉此行。


    台北

    作为台湾第一大城市,台北已经其下属的新北、北投、桃园城市带,几乎覆盖了整个台湾岛北部。行走在台北街头,能明显感受到新老两片城区的区别,新城区遍布现代化的商业街和办公楼、高档公寓,而老城区就像是90年代的上虞,低矮的民用住房,配上街边连绵不绝的店面,还有呼啸而过的机车。

    101大楼一直是台北的地标,而我个人又是一个高楼控,喜欢上各种大楼上打卡,但是似乎每个大楼上俯瞰城市skyline的味道都各有不同。芝加哥是那种高楼墙,紧密地围着密歇根湖,纽约是那种高楼林,密密麻麻地插满了整个曼哈顿,上海是陆家嘴几栋高楼不分伯仲,然后一点点坡度下降延伸出去。而台北则像是一张披萨饼,整个城市目之所及之处就没能在高度上接近101的,而101恰似是这张巨大的披萨饼上的一根蜡烛。

    很遗憾没去象山拍摄出最佳的101全景,只能从信义坊出来找了个角度拍了一下。

    从101出来还去了著名的信义坊诚品书店,环境极佳,现在这种高颜值的书店在世界各地都越来越有市场。


    自由广场

    台北自由广场可能是最著名的一个景点,布局像极了北京天安门广场,只不过没那么大。白底蓝瓦的建筑满满的是国民党的味道。中心轴的两侧是国家大剧院和国家音乐厅,是仿清宫风格。

    中正纪念堂位于自由广场的最东面,完完全全是仿照着南京中山陵来建的,只不过一个圆一个方。一般来说中国人讲究坐北朝南,而蒋先生却选了坐东望西,一开始我也没想到这是为何,后来反应过来,西面,那是大陆啊。

    纪念堂内部跟美国林肯纪念堂布局类似,很幸运在整点的时候看到了卫兵交接仪式。相比于解放军礼仪队而言,国军的仪式显得复杂而繁重,在纪念堂里前前后后连踏步带比划进行了约莫十几分钟。


    九份

    台北到九份约40公里,坐九份专线过去要一小时30分钟,而且因为我不想提着行李到处走,只能当天去当天回来,因此我一直在犹豫要不要把它加入行程,最后心一横还是去了。因为“九份的咖啡店”和千与千寻的缘故,九份对我来说一直充满了憧憬。从之前的矿山到现在的旅游景点,这里每家店、每道弯似乎都有故事。

    从这个观景台看出去能看到远方的基隆港。感觉基隆港整个一个是台湾近代史的见证人,当时的荷兰人从这上来,郑成功从这上来,施琅从这上来,再到后来侵华日军从这上来,国军光复从这上来,民国政府退出大陆后还是从这上来。

    当夜幕降临,九份老街上的每一家店前都亮起了灯笼,这才是九份最美的样子。

    芋圆是九份最有名的小吃,我在赖阿婆家稍作休息,店面很破旧,却耐不住这是九份最有名的店家。

    阿妹茶楼是千与千寻婆婆的小楼的原型,因此九份也是日本游客的最爱,同车的人里,至少有一般是日本游客,九份的店家都会说一口流利的日语。


    台北故宫博物院

    之前常常会听到一种说法,国府退到台湾时候从北京故宫搬走了大量珍贵的国宝放到台湾保存,让这些国宝躲过了新中国建国后的各种动荡,是中华文明保存和发扬的功臣。另一种说法是,国府在撤退过程中因为保存不当,导致大量国宝破损和遗失,而到了台湾之后,由于保存手段跟不上,又对一些国宝造成了严重损害,是民族的罪人。但是不可否认的是,台北故宫博物院确实是一个值得深度游的地方,因为这里有太多太多原本只在课本上才能看到的珍宝。

    一直以来的说法是,台北故宫有三宝:毛公鼎、大白菜和东坡肉,因为展品轮休的关系,东坡肉没有展出,而毛公鼎和五花肉前果然人流络绎不绝。毛公鼎作为中国历史上发现的铭文最多的青铜器,确实很有价值,但是事实上,大白菜的艺术造诣并没有特别高,只是一个很普通的宫廷工艺品而已。

    虽然台北故宫有为数不少的玉器、瓷器和青铜器,但是因为当初南逃仓促,带不了很多占地面积大,又很重的藏品,所以在运输过程中,这部分的损伤也最为严重。而台北故宫中最最值得看的,其实是那些便于携带的字画。

    著名的半截《富春山居图》

    书神王羲之的《快雪时晴帖》

    苏轼的《食寒帖》

    褚遂良的《倪宽赞》

    其实当时在台北故宫的2号馆正在进行法国奥赛博物馆的展览,有很多印象派大师的作品,可惜时间有限没能去参观了。


    台湾夜市

    一直知道台湾的夜市和小吃非常出名,这趟来也赶了不少夜市,比如士林夜市、宁夏夜市。总的来说感觉是大同小异。呆的时间最久的当然是垦丁大街的垦丁夜市,满大街都是密密麻麻的行人。垦丁夜市最有特点的就是沿街的性感卖酒辣妹,不过说实话,价格明显偏贵,不如街边正经的调酒摊或者酒吧来的好喝实惠。垦丁大街边上的酒吧真的要好评一波,我在别的地方从没买到过那么便宜的酒,而且味道很有垦丁特色。

    一些之前台湾传的非常有名的小吃,如大肠包小肠、蚵仔煎对于我来说似乎并没有那么大的吸引力。倒是有两样东西特别让我特别留恋,一个是台湾的奶茶,每一家不起眼的小店似乎都有特色,尝到之前从没品尝过的味道,而且用料,尤其是珍珠和奶都很地道。另一个是臭豆腐,因为是绍兴人的缘故,所以对臭豆腐有特别的爱,而我在台湾几乎吃遍了各种臭豆腐,各种稀奇古怪的做法,回味无穷。


    最美的风景是人

    之前就接触过好多来自台湾的朋友,他们给我留下最深刻的印象就是特别热情话多,尤其是他们操着台湾腔跟我聊天聊地的时候,特别有趣。这一趟出行,很遗憾的是没能真的沉下来坐着跟当地人聊聊天,但是也接触了很多有趣的人,机场里的台中阿姨、民宿楼下的酒保小哥、潜水店老板、潜水教练老徐、生鱼片排档老板娘、夜市摊主、纪念品店老板娘、特产店老板、总统府导游等等,他们有的是当年撤退国军的后代、有的是原住民、甚至有的是出生在韩国定居在台湾的山东人。我很乐意坐下来听他们聊他们的生活、他们生活的土地、他们对对岸的看法。

    总的来说,台湾普通民众,尤其是吃旅游饭的那些人,对于对岸基本都是肯定态度,事实也证明,庞大的陆客群体给台湾带来的旅游收益远远高于东南亚以及日韩游客。而媒体上,至少就我在电视和报纸上看到的,对于对岸的描述也基本符合事实,虽然一些绿营媒体也会吹捧一下香港黄之峰之流来恶心一下大陆,但绝大多数都认识到了两岸之间的经济和军事上的差距,较为客观地描述着一些新闻事件。比较有代表性的就是台湾媒体对于当时是否购置美国F-35战斗机、川普和习近平会面几个热点新闻的评述。


    后记

    暂且先写到这些,以后有想到的在另为补充。

    ]]>