numpy学习

Numpy

学习动机

哪哪都用numpy!python数据处理不可能用不到他!

概览

  • 核心类型是ndarray对象

  • ndarray数组大小不可变(更改ndarray的大小将创建一个新的数组并删除原始数据),封装了同质数据类型的n维数组。同质要求数组中存储相同的数据类型(这使得在存储器中他们将具有相同的大小)。但是可以通过使用对象类型的数组来间接打破这一限制。

  • udarray的优势在于以python的代码长度达到c的运行速度,做到这一点的技术支持是NumPy内部预编译的c实现。这反映它的大部分功能的基础:矢量化和广播
  • 矢量化描述了在代码中没有任何显式循环:
  • 广播就是指对矢量进行的操作“广播”至他的每一个元素。

基础

数据类型

数据类型如下图:

  • 很多构造函数可以显示指定数据类型,如arange(7,dtype=uint16)
  • 大部分类型之间可以强制类型转换,如:float64(),复数的类型转换会受到限制。
  • 由于ndarray的同质性,可以方便的计算ndarray的占用空间,也可以使用a.dtype.itemsize查看内个数组元素在内存中占用的字节数。
  • numpy的数据类型是dtype类,这个类也有属于自己的方法和属性。

ndarray数组

Read More

使用CRF++进行分词

安装CRF++

在这里下载CRF++源码

1
2
3
4
5
6
7
8
tar zxvf CRF++-0.58.tar.gz
cd CRF++-0.58
./configure
make # 如果path.h报错 加上#inlcude<iostream>头文件
sudo make install
cd python
python setup.py build
sudo python setup.py install

然后进入python,尝试import CRFPP可能出现问题:UnboundLocalError: local variable 'fp' referenced before assignment,原因就是fp没有定义,添加fp = None。改动之后再次执行又出现问题:ImportError: No module named _CRFPP,那么就对ImportException进行处理,截至到这里,把CRFPP.py中的swig_import_helper函数改动如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def swig_import_helper():
from os.path import dirname
import imp
fp = None
try:
fp, pathname, description = imp.find_module('_CRFPP', [dirname(__file__)])
_mod = imp.load_module('_CRFPP', fp, pathname, description)
except ImportError:
import _CRFPP
return _CRFPP
finally:
<!-- more -->
if fp is not None: fp.close()
return _mod

然后再运行,又会出现问题:ImportError: libcrfpp.so.0: cannot open shared object file: No such file or directory,这次回到shell执行:

1
sudo ln -s /usr/local/lib/libcrfpp.so.0 /usr/lib

此时再次import,成功执行。

下载人民日报语料库

点击下载
下载得到的语料已经经过分词和词性标注,形如:

19980101-01-001-005/m 同胞/n 们/k 、/w 朋友/n 们/k 、/w 女士/n 们/k 、/w 先生/n 们/k :/w
19980101-01-001-006/m 在/p 1998年/t 来临/v 之际/f ,/w 我/r 十分/m 高兴/a 地/u 通过/p [中央/n 人民/n 广播/vn 电台/n]nt 、/w [中国/ns 国际/n 广播/vn 电台/n]nt 和/c [中央/n 电视台/n]nt ,/w 向/p 全国/n 各族/r 人民/n ,/w 向/p [香港/ns 特别/a 行政区/n]ns 同胞/n 、/w 澳门/ns 和/c 台湾/ns 同胞/n 、/w 海外/s 侨胞/n ,/w 向/p 世界/n 各国/r 的/u 朋友/n 们/k ,/w 致以/v 诚挚/a 的/u 问候/vn 和/c 良好/a 的/u 祝愿/vn !/w

对预标注预料进行预处理,使用下面的Python脚本“pre.py”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#coding=utf8

import sys

home_dir = "./"
def splitWord(words):
uni = words.decode('utf-8')
li = list()
for u in uni:
li.append(u.encode('utf-8'))
return li

#4 tag

#S/B/E/M
def get4Tag(li):
length = len(li)
#print length
if length == 1:
return ['S']
elif length == 2:
return ['B','E']
elif length > 2:
li = list()
li.append('B')
for i in range(0,length-2):
li.append('M')
li.append('E')
return li
#6 tag
#S/B/E/M/M1/M2
def get6Tag(li):
length = len(li)
#print length
if length == 1:
return ['S']
elif length == 2:
return ['B','E']
elif length == 3:
return ['B','M','E']
elif length == 4:
return ['B','M1','M','E']
elif length == 5:
return ['B','M1','M2','M','E']
elif length > 5:
li = list()
li.append('B')
li.append('M1')
li.append('M2')
for i in range(0,length-4):
li.append('M')
li.append('E')
return li

def saveDataFile(trainobj,testobj,isTest,word,handle,tag):
if isTest:
saveTrainFile(testobj,word,handle,tag)
else:
saveTrainFile(trainobj,word,handle,tag)

def saveTrainFile(fiobj,word,handle,tag):
if len(word) > 0:
wordli = splitWord(word)
if tag == '4':
tagli = get4Tag(wordli)
if tag == '6':
tagli = get6Tag(wordli)
for i in range(0,len(wordli)):
w = wordli[i]
h = handle
t = tagli[i]
fiobj.write(w + '\t' + h + '\t' + t + '\n')
else:
#print 'New line'
fiobj.write('\n')

#B,M,M1,M2,M3,E,S
def convertTag(tag):
fiobj = open( home_dir + 'people-daily.txt','r')
trainobj = open( home_dir + tag + '.train.data','w' )
testobj = open( home_dir + tag + '.test.data','w')

arr = fiobj.readlines()
i = 0
for a in arr:
i += 1
a = a.strip('\r\n\t ')
print "debug_a:",a
if a=="":continue
words = a.split(" ")
test = False
if i % 10 == 0:
test = True
for word in words:
print "---->", word
word = word.strip('\t')
if len(word) > 0:
i1 = word.find('[')
if i1 >= 0:
word = word[i1+1:]
i2 = word.find(']')
if i2 > 0:
w = word[:i2]
word_hand = word.split('/')
print "----",word
w,h = word_hand
#print w,h
if h == 'nr': #ren min
#print 'NR',w
if w.find('·') >= 0:
tmpArr = w.split('·')
for tmp in tmpArr:
saveDataFile(trainobj,testobj,test,tmp,h,tag)
continue
if h != 'm':
saveDataFile(trainobj,testobj,test,w,h,tag)
if h == 'w':
saveDataFile(trainobj,testobj,test,"","",tag) #split

trainobj.flush()
testobj.flush()

if __name__ == '__main__':
if len(sys.argv) < 2:
print 'tag[6,4] convert raw data to train.data and tag.test.data'
else:
tag = sys.argv[1]
convertTag(tag)

这里用到的分词方法是由字构词(基于字标注)的分词方法(Character-based tagging)。
该方法由N. Xue(薛念文) 和 S. Converse 提出, 首篇论文发表在2002年第一届国际计算语言学学会(ACL)汉语特别兴趣小组 SIGHAN (http://www.sighan.org/) 组织的汉语分词评测研讨会上[Xue and Converse, 2002]。基本思想:将分词过程看作是字的分类问题:每个字在构造一个特定的词语时都占据着一个确定的构词位置(即词位)。一般情况下,每个字只有4个词位:词首(B)、词中(M)、词尾(E)和单独成词(S) 。
该脚本接受一个参数,该参数只能是6或者4,参数为4的时候将单字对应到上述4类标签,参数为6的时候实际上是对上述4tags的一种扩展,词中字可能有$$$M_1/M_2/M$$$三种标签,三个字的词标记为$$$BME$$$,四个字标记为$$$BM_1ME$$$,五个字标记为$$$BM_1M_2ME$$$,以此类推。这是一种拓展的思路,条件随机场只是去预测标签,究竟设置什么样的标签,标签有什么意义,是要靠人工赋予。
得到下面的格式,第一列是单字,第二列是词性信息,第三列是基于字标注的分词信息:

迈    v    B
向    v    E
充    v    B
满    v    E
希    n    B
望    n    E
的    u    S
新    a    S
世    n    B
纪    n    E
—    w    B
—    w    E
一    t    B
九    t    M1
九    t    M2
八    t    M
年    t    E
新    t    B
年    t    E
讲    n    B
话    n    E
(    w    S
附    v    S
图    n    B
片    n    E
张    q    S
)    w    S

使用CRF++进行训练和预测

自定义特征模板template

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# Unigram
U00:%x[-2,0]
U01:%x[-1,0]
U02:%x[0,0]
U03:%x[1,0]
U04:%x[2,0]
U05:%x[-2,0]/%x[-1,0]/%x[0,0]
U06:%x[-1,0]/%x[0,0]/%x[1,0]
U07:%x[0,0]/%x[1,0]/%x[2,0]
U08:%x[-1,0]/%x[0,0]
U09:%x[0,0]/%x[1,0]

U10:%x[-2,1]
U11:%x[-1,1]
U12:%x[0,1]
U13:%x[1,1]
U14:%x[2,1]

U15:%x[-1,0]/%x[1,0]
U16:%x[-1,1]/%x[1,1]

U17:%x[-1,1]/%x[0,1]
U18:%x[0,1]/%x[1,1]

U19:%x[-2,1]/%x[-1,1]/%x[0,1]
U20:%x[-1,1]/%x[0,1]/%x[1,1]
U21:%x[0,1]/%x[1,1]/%x[2,1]

# Bigram
B

CRF++有两种模板类型:

  1. Unigram类型

    每一行%x[#,#]生成一个CRFs中的点(state)函数: f(s, o), 其中s为t时刻的的标签(output),o为t时刻的上下文.如CRF++说明文件中的示例函数:

    func1 = if (output = B-NP and feature=”U01:DT”) return 1 else return 0

    它是由U01:%x[0,1]在输入文件的第一行生成的点函数.将输入文件的第一行”代入”到函数中,函数返回1,同时,如果输入文件的某一行在第2列也是DT,并且它的output同样也为B-NP,那么这个函数在这一行也返回1.

  2. Bigram类型

    每一行%x[#,#]生成一个CRFs中的边(Edge)函数:f(s’, s, o), 其中s’为t - 1时刻的标签.也就是说,Bigram类型与Unigram大致机同,只是还要考虑到t - 1时刻的标签.如果只写一个U的话,默认生成f(s’, s).

    模板文件中的每一行是一个模板。每个模板都是由%x[row,col]来指定输入数据中的一个token。row指定到当前token的行偏移,col指定列位置。

训练模型

训练

1
crf_learn template_file train_file model_file
这个训练过程的时间、迭代次数等信息会输出到控制台上(感觉上是crf_learn程序的输出信息到标准输出流上了),如果想保存这些信息,我们可以将这些标准输出流到文件上,命令格式如下:
1
crf_learn template_file train_file model_file >> train_info_file

输出的信息如下。其中各个域的信息是:

  • iter: 迭代处理的次数
  • terr: 标记错误率。
  • serr: 句子错误率。
  • obj: 目标函数的值。
  • diff: 目标函数相对上一次的相对变化率。
1
2
3
4
5
6
7
8
9
10
Number of sentences: 155657
Number of features: 16586178
Number of thread(s): 1
Freq: 1
eta: 0.00010
C: 1.00000
shrinking size: 20
iter=0 terr=0.67485 serr=1.00000 act=16586178 obj=2754679.67614 diff=1.00000
iter=1 terr=0.39541 serr=0.67567 act=16586178 obj=2148688.44746 diff=0.21999
iter=2 terr=0.39131 serr=0.67553 act=16586178 obj=1564786.70817 diff=0.27175

对于其中的参数,有四个主要的参数可以调整:

  1. -a CRF-L2 or CRF-L1

    规范化算法选择。默认是CRF-L2。一般来说L2算法效果要比L1算法稍微好一点,虽然L1算法中非零特征的数值要比L2中大幅度的小。

  2. -c float

    这个参数设置CRF的hyper-parameter。c的数值越大,CRF拟合训练数据的程度越高。这个参数可以调整过度拟合和不拟合之间的平衡度。这个参数可以通过交叉验证等方法寻找较优的参数。

  3. -f NUM

    这个参数设置特征的cut-off threshold。CRF++使用训练数据中至少NUM次出现的特征。默认值为1。当使用CRF++到大规模数据时,只出现一次的特征可能会有几百万,这个选项就会在这样的情况下起到作用。

  4. -p NUM

    如果电脑有多个CPU,那么那么可以通过多线程提升训练速度。NUM是线程数量。

测试

1
crf_test -m model_file test_files

有两个参数-v和-n都是显示一些信息的,-v可以显示预测标签的概率值,-n可以显示不同可能序列的概率值,对于准确率,召回率,运行效率,没有影响,这里不说明了。

与crf_learn类似,输出的结果放到了标准输出流上,而这个输出结果是最重要的预测结果信息(测试文件的内容+预测标注),同样可以使用重定向,将结果保存下来,命令行如下。

1
crf_test -m model_file test_files >> result_file

评估模型

python脚本“score.py”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys

if __name__=="__main__":
try:
file = open(sys.argv[1], "r")
except:
print "result file is not specified, or open failed!"
sys.exit()

wc_of_test = 0
wc_of_gold = 0
wc_of_correct = 0
flag = True

for l in file:
if l=='\n': continue

_, _, g, r = l.strip().split()

if r != g:
flag = False

if r in ('E', 'S'):
wc_of_test += 1
if flag:
wc_of_correct +=1
flag = True

if g in ('E', 'S'):
wc_of_gold += 1

print "WordCount from test result:", wc_of_test
print "WordCount from golden data:", wc_of_gold
print "WordCount of correct segs :", wc_of_correct

#查全率
P = wc_of_correct/float(wc_of_test)
#查准率,召回率
R = wc_of_correct/float(wc_of_gold)

print "P = %f, R = %f, F-score = %f" % (P, R, (2*P*R)/(P+R))

然后运行python score.py test-info.txt,有如下输出:

1
2
3
4
WordCount from test result: 102690
WordCount from golden data: 102952
WordCount of correct segs : 101950
P = 0.992794, R = 0.990267, F-score = 0.991529`

Spark初探(一)——————基于矩阵分解的题目推荐系统

数据获取

调研

在数据驱动的工作方面,获取数据是工作的第一步,高质量的数据来源又会使得我们的工作事半功倍。对于题目推荐系统,需要的数据自然是大量的题库和用户对于题目的做题情况。首先想到的是类似于猿题库或者易题库或者之类的公司,他们做的是初中高中等试题的推荐系统。然而通过调研发现这些公司的试题来源是有专门的部门进行人工输入,用户做题情况来自于大量的用户对其公司产品的使用情况,这些公司也没有针对开发者的公开接口。而网络上可以获取到的题库大部分零零散散,文件格式不统一,题目格式不统一,存在大量的内容重复或者答案缺失问题。而且下载得到题目数据也没有用户的做题情况,于是便考虑使用爬虫爬取针对性的题目数据和用户做题情况。

在调研的过程中注意到目前有很多免费的ACM题库的OpenJudge平台,这些平台上可以以HTML的形式方便的获取题目列表和用户提交信息,并且格式统一,可读性强,缺失率低,便于进行数据预处理。

Scrapy爬虫框架

可以使用的开源爬虫项目不胜枚举,其中Java和Python为语言的爬虫框架为主流,比如Nutch、Crawler4j、WebMagic、scrapy、WebCollector等等,其中Nutch是Apache维护的开源项目,是一个为了搜索引擎服务的强大工具。除了国外的很多优秀程序员开发的爬虫,国内也出现了很多不错的开源项目。

Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架。 可以应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。

所谓网络爬虫,可以抓取特定网站网页的HTML数据。抓取网页的一般方法是,定义一个入口页面,然后一般一个页面会有其他页面的URL,于是从当前页面获取到这些URL加入到爬虫的抓取队列中,然后进入到新页面后再递归的进行上述的操作,其实说来就跟深度遍历或广度遍历一样。Scrapy 使用 Twisted这个异步网络库来处理网络通讯,架构清晰,并且包含了各种中间件接口,可以灵活的完成各种需求。由于我们的爬虫任务比较简单,因此Scrapy是一个不错的选择。下图是Scrapy的架构和处理流程。

目标网站:南洋理工OJ平台

国内有很多OpenJudge的平台,其中,北京大学 Online Judge(POJ)建立较晚,但题目加得很快,现在题数和ZOJ不相上下,特点是举行在线比赛比较多,数据比ZOJ上的要弱,有时候同样的题同样的程序,在ZOJ上WA,在POJ上就能AC。不过感觉pku的题目要难很多。这个题库的一大特点就是 OnlineJudge功能强大,其实pku现在已经是中国最好的ACM网站。浙江大学 Online Judge(ZOJ, 国内最早也是最有名气的OJ,有很多高手在上面做题。打开速度快。而我们选择的目标站点是南洋理工ACM,原因是这个网站较为轻量级,接口清晰,提交频率较快。下图是南洋理工ACM的首页。

下图是ACM的用户提交情况。

数据分析

推荐系统最重要的就是用户效用矩阵,所以上图中用户提交信息中必须获得的field是用户id,题目id和结果。其他信息也一并或取以备后用。而用户对题目得分这里并没有用户直接对题目的评分,只有通过为Accept或者没有通过时出现的各种错误,如Output Limit Exceed/Runtime error/Compilation Error/Wrong Answer等等。因此这一项需要人工处理。

我们的考虑是如果用户对一道题多次提交并且最后成功通过,则认为该用户对该题评分较高,如果只需要一两次提交便通过或者多次提交未能通过,此时的评分较低,因此User-Item的效用是该用户对该题的所有提交记录的线性加权和的对数。

下面是数据schema和前几条实例:

我们爬取到的数据是用户的做题记录,而需要喂给算法的数据是user和item之间的效用矩阵,那么怎么给用户定这个分数呢。这里其实我就是拍脑门相处的方法,目标是推荐给用户新的用户想做并且黑有一些难度的题目。我们考虑到题目提交结果有这么几种可能性:CompileError、MemoryOutOfBound、WrongAnswer、Accept,因此用户对题目的分数就设定为一个Accept5分,OOM或者超时都是3分,错误答案是2分,其他是一分,最后取对数。

最后得到的效用矩阵是一个4777*860的矩阵,其中只有0.8%的元素不为零。

推荐算法

1. 基于矩阵分解的用户推荐算法

用户对物品的打分行为可以表示成一个评分矩阵A(m*n),表示m个用户对n各物品的打分情况。如下图所示:

其中,A(i,j)表示用户user i对物品item j的打分。但是,用户不会对所以物品打分,图中?表示用户没有打分的情况,所以这个矩阵A很多元素都是空的,也就是这个矩阵是一个非常稀疏的矩阵,我们称其中的空项为缺失值(missing value)。在推荐系统中,我们希望得到用户对所有物品的打分情况,如果用户没有对一个物品打分,那么就需要预测用户是否会对该物品打分,以及会打多少分。这就是所谓的矩阵补全,将原本没有分数的矩阵填上我们的预测值,然后找到一个用户原本所有空项的位置上的较大的值对应的item即完成了推荐。

Latent Factor Models的核心就是下面这个假设:打分矩阵A是近似低秩的。换句话说,一个 m n 的打分矩阵 A 可以用两个小矩阵U(m\k)和V(k*n)的乘积来近似。换句话说,如果对矩阵进行SVD,那么绝大部分的能量应该集中在某几个较大的奇异值上,剩下的奇异值可能表示模型中的噪声或者用户的一些个性化等等因素。

这样我们就把整个系统的自由度从m×n一下降到了(m+n)*k。我们接下来就聊聊为什么 ALS 的低秩假设是合理的。世上万千事物,人们的喜好各不相同。但描述一个人的喜好经常是在一个抽象的低维空间上进行的,并不需要把其喜欢的事物一一列出。举个例子,我喜欢看世界观完整的硬科幻作品,不喜欢比较含蓄的言情作品,那么大家根据这个描述就知道我大概会喜欢《黑客帝国》胜过《E.T.》,但不管怎样我对这两个电影的打分应该超过平均分,但是如果让我看"爱在三部曲",我可能会无聊的睡着。也就是说,人们的喜好宏观上讲不是针对各个作品的,而是针对各个流派的,而流派或者风格的种类数要远远小于作品实例的种类数。我们可以把人们的喜好和电影的特征都投到这个低维空间,一个人的喜好映射到了一个低维向量$$$u_{person}$$$,一个电影的特征变成了纬度相同的向量$$$u_{movie}$$$ ,那么这个人和这个电影的相似度就可以表述成这两个向量之间的内积 我们把打分理解成相似度,那么打分矩阵A(m*n)就可以由用户喜好特征矩阵U(m*k)产品特征矩阵V(n*k)的乘积来近似表示,这个 k 就是模型中隐含因子的个数。

2. 最小交替二乘法ALS

矩阵分解模型的损失函数为:
$$C = \sum_{i,j \in R}[(a_{i,j}-u_iv_j^T)+\lambda(u_i^2+v_j^2)]$$

有了损失函数之后,下面就开始谈优化方法了,通常的优化方法分为两种:交叉最小二乘法(alternative least squares)和随机梯度下降法(stochastic gradient descent)。在spark中使用交叉最小二乘法(ALS)来最优化损失函数。算法的思想就是:我们先随机生成 $$$U^{(0)}$$$, 然后固定它求解 $$$V^{(0)}$$$。

对于具体的优化算法,没有深入研究,直接看参考博文中的推导就行。

3. Spark mllib中的ALS算法的使用

我们考虑的是使用spark的mllib来完成推荐系统的实现,一个原因是spark虽然不是专业的机器学习算法工具,但是目前hadoop生态与安静的蓬勃发展使得这种分布式计算平台大有用武之地,且spark在hadoop上的应用,大有为大象插上翅膀的效果。应该说spark的在使用上面的经济成本,性能优势,一站式解决能力,一定会使其大放异彩。机器学习算法一般都有很多个步骤迭代计算的过程,机器学习的计算需要在多次迭代后获得足够小的误差或者足够收敛才会停止,迭代时如果使用Hadoop的MapReduce计算框架,每次计算都要读/写磁盘以及任务的启动等工作,这回导致非常大的I/O和CPU消耗。而Spark基于内存的计算模型天生就擅长迭代计算,多个步骤计算直接在内存中完成,只有在必要时才会操作磁盘和网络,所以说Spark正是机器学习的理想的平台。

而MLlib 构建在apache spark之上,一个专门针对大量数据处理的通用的、快速的引擎。MLlib 是 Spark的可以扩展的 机器学习库,由以下部分组成:通用的学习 算法和工具类,包括分类,回归,聚类,协同过滤,降维,当然也包括调优的部分。

首先进行数据预处理将前面提到的json数据进行预处理,并且由RDD转换为DataFrame数据类型,这种数据类型相比RDD来说最显著的优势是具有schema,可以提供访问与处理数据的更强大的接口。其中DataFrame中的元素是一个三元组<userID, questionID, score>,实际上就是矩阵中的一个值。

然后我们可以调用ML中已经实现好的方法来进行计算。

这个实际上是建立了一个ALS计算框架,其中需要程序员配置的参数有如下几个。

  • numBlocks 是用于并行化计算的用户和商品的分块个数 (默认为10)。
  • rank 是模型中隐语义因子的个数(默认为10)。
  • maxIter 是迭代的次数(默认为10)。
  • regParam 是ALS的正则化参数(默认为1.0)。 主要用于控制模型的拟合程度,增强模型泛化能力。取值越大,则正则化惩罚越强。大型推荐系统一般需要调参得到合适的值。
  • rank:隐含因子数目。这个值会影响矩阵分解的性能,越大则算法运行的时间和占用的内存可能会越多。通常需要进行调参,一般可以取10-200之间的数。
  • implicitPrefs 决定了是用显性反馈ALS的版本还是用适用隐性反馈数据集的版本(默认是false,即用显性反馈)。
  • alpha 是一个针对于隐性反馈 ALS 版本的参数,这个参数决定了偏好行为强度的基准(默认为1.0)。
  • nonnegative 决定是否对最小二乘法使用非负的限制(默认为false)。

其中需要说明的是显性反馈和隐性反馈的区别:显性反馈行为:用户明确表示对物品喜好的行为。隐性反馈行为:不能明确反映用户喜好的行为。

下图是显性反馈数据和隐形反馈数据的比较

显性反馈数据 隐性反馈数据
用户兴趣 明确 不明确
数量 较少 庞大
存储 数据库 分布式文件系统
实时读取 实时 有延迟
正负反馈 都有 只有正反馈

下图是各代表应用中显性反馈数据和隐性反馈数据的例子

显性反馈 隐性反馈
视频网站 用户对视频的评分 用户观看视频的日志、浏览视频页面的日志
电子商务网站 用户对商品的评分 购买日志、浏览日志
门户网站 用户对新闻的评分 阅读新闻的日志
音乐网站 用户对音乐/歌手/专辑的评分 听歌的日志

得到模型后使用fit方法进行模型的训练,然后使用transform进行测试集的测试或者新数据的预测。Model可以保存下来用于实时计算,后台的模型计算比较耗费时间。

参考

  1. ALS矩阵分解算法应用
  2. 深入理解Spark ML:基于ALS矩阵分解的协同过滤算法与源码分析
  3. 推荐系统中的矩阵分解技术

总是学不会的Vim

自定义bash配置

修改/etc/vim/vimrc文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

" 开启语法高亮
syntax enable
syntax on

" 自动缩进设置
set tabstop=4
" 设置自动对齐空格数
set shiftwidth=4
set expandtab
set autoindent
"智能缩进
set smartindent

" 显示行号
set number

" 检测文件类型
filetype on

" 文件修改之后自动读入
set autoread

" 显示当前行号和列号
set ruler

" " 在状态栏显示正在输入的命令
set showcmd

" " 左下角显示当前Vim模式
set showmode

"搜索结果高亮显示
set hlsearch
set incsearch " 输入搜索内容时就显示搜索结果

autocmd InsertLeave * se nocul " 用浅色高亮当前行
autocmd InsertEnter * se cul " 用浅色高亮当前行

set cursorline " 突出显示当前行

频繁操作

  1. o:光标所在行下面插入一行
  2. 在vim外ctrl+C复制的内容不能用p粘贴
  3. dd:删除一行,dw:删除一个单词,df×:删除光标到×中间的东西(包括×)
  4. :>:<是对当前缩进,gg=G是全局代码格式化
  5. 类似于 sed 的替换指令。

插件安装

  • vundle
  • vim-scripts/indentpython.vim
  • vim-syntastic/syntastic

VIM - 自动补全插件 jedi-vim

RESTful API 设计指南

转载自: http://www.ruanyifeng.com/blog/2014/05/restful_api.html
作者: 阮一峰

日期: 2014年5月22日

网络应用程序,分为前端和后端两个部分。当前的发展趋势,就是前端设备层出不穷(手机、平板、桌面电脑、其他专用设备……)。

因此,必须有一种统一的机制,方便不同的前端设备与后端进行通信。这导致API构架的流行,甚至出现“API First”的设计思想。RESTful API是目前比较成熟的一套互联网应用程序的API设计理论。我以前写过一篇《理解RESTful架构》,探讨如何理解这个概念。

今天,我将介绍RESTful API的设计细节,探讨如何设计一套合理、好用的API。我的主要参考了两篇文章(12)。

RESTful API

一、协议

API与用户的通信协议,总是使用HTTPs协议

二、域名

应该尽量将API部署在专用域名之下。

1
https://api.example.com

如果确定API很简单,不会有进一步扩展,可以考虑放在主域名下。

1
https://example.org/api/

Read More

本站总访问量