文科汪入门「机器学习」

作者: AntonyWong | 来源:发表于2017-07-13 00:44 被阅读837次

    这个标题有点哗众取宠了。文科汪是不假,不过我有三年的移动(iOS)开发经验。

    不过之前也没有接触过「机器学习」,所以也尽量深入浅出,希望真的是文科生也看得懂。

    学习资源简介

    机器学习的相关学习资料汗牛充栋,很多有意学习的朋友被淹没在浩瀚的资料中,不明所以。因此,找到适合自己程度的资料是很关键的。

    最近在coursera上学习了一个关于机器学习方面的入门课程Machine Learning Foundations: A Case Study Approach(机器学习基础:个案研究法)。是华盛顿大学的一门线上公开课。

    这个课程的特别之处,是从具体的案例(项目)入手,去介绍机器学习、深度学习。比起纯讲理论、晦涩难懂的课程,友好太多。(之前看吴恩达的机器学习课程,就没有坚持看完——我程度太低)。这个课程从以下5个案例(项目)展开,分别介绍相关理论知识:

    • 预测房价(Predicting house prices)
      • 引出Regression(回归)
    • 从产品评论中判断买家态度(Analyzing the sentiment of product reviews)
      • 引出Classification(分类)
    • 检索维基百科的文章(Retrieving Wikipedia articles)
      • 引出Clustering(聚类)
    • 推荐歌曲(Recommending songs)
      • 引出Collaborative fitering(协同过滤)
    • 利用深度学习给图片分类(识别图片)(Classifying images with deep learning)
      • 引出Deep learning(深度学习)、Neural networks(神经网络)

    主要概念及环境搭建

    什么是机器学习

    「机器学习(Machine Learning)」这个词,1959年由美国的计算机科学家Arthur Samuel提出。

    但是看完维基百科晦涩的定义后,也没搞清楚「机器学习(Machine Learning)」的具体定义。就用coursera课程中的一张流程图来理解什么是机器学习。

    机器学习示意图

    从这张流程图可以看到,首先,要有数据,把数据喂给某个算法(或者叫模型)进行「学习」,使其具有解决某种问题的能力(智力),我把这个过程,理解为「机器学习」。

    以前的软件,是程序员写好一系列的规则,运行的时候按照这些规则来执行、解决问题。而软件加入「机器学习」之后,可以从喂入的数据中发现规则、学习规则,然后解决问题,这样看起来,软件就比以前显得智能多了。

    可以看到,数据是前提条件,这也解释了为什么「机器学习」在1959年提出,到现在才「火」起来,因为现在各行各业产生的数据,都数字化了,产生了足够多的数据来应用到「机器学习」中。

    吴恩达在接受WSJ采访是也说过类似的观点:

    你所在的行业或者你关注的行业是否会受到AI的影响,这里有一个模式或许有用。首先:这个行业资料已数字化,意味着活动流程转移到计算机上。这就产生了数据,给了AI运用数据更加智能化处理业务的机会。

    什么是深度学习,与机器学习的异同

    「深度学习(Deep Learning)」,首先,它还是属于「机器学习」的范畴,是一种特殊的「机器学习」。接下来也是通过课程中的两张图来简单理解(这是个人理解,并不一定准确):

    传统的机器学习,特征是「线性」的

    可以看到,上面这张图,是传统的「机器学习」过程:1.喂数据;2.学习(训练模型/算法);3.解决问题(这里解决的实际问题,是通过买家对产品的评论,判断买家是正面评价还是负面评价——后面会详细介绍)

    传统的「机器学习」,是直接抽取数据的某些「特征(属性)」(这些「特征」被称为是「线性」的),最终形成问题的解决方案。

    深度学习,基于「神经网络」,并利用「非线性特征」

    而有些问题(课程举例的是「XOR问题」),不能通过传统的「机器学习」解决,也就是说不能直接从「线性」的「特征」得出问题的解决方案。

    这时候,「非线性特征(non-linear features)」提供了问题的解决方向,而「『神经网络(neural networks)』则提供了一种非线性的数据表现形式」。

    如上图,我们不能直接通过数据的「特征」(x1, x2)得到问题的答案,所以对数据的「特征」进行多层次的变换,得到一些「非线性特征」(z1, z2)——「神经网络」中的一层——最终形成问题的解决方案。

    图片中的这种数据结构,叫做「图(graphs)」,「神经网络」就是用这种数据结构来表示的。图片中的是一个3层的神经网络,中间还可以有若干层。

    「神经网络」,本质上就是对数据进行多层次的变换。

    综上,我们把上面这个过程,叫做深度学习——个人理解。另外,因为「深度学习」是构建在「神经网络」基础之上的,所以也有人将「深度学习」和「神经网络」等同起来。

    备注:「深度学习」又叫「hierarchical learning(分层学习、多层次学习)」,我觉得这种叫法更能直观反映这个学习过程。

    环境搭建及基本操作

    Python

    此课程使用编程语言Python(python的中文意思:大蟒蛇)

    Python可以很方便地操作数据,并有很多第三方工具可供使用,让你可以轻松构建想要的应用程序。(R语言是另一种可供替代的语言)

    iPython Notebook

    Python的编程环境。界面如下(操作系统:macOS):

    iPython Notebook的目录界面——是在浏览器中打开的

    GraphLab Creat和SFrame

    GraphLab CreatSFrame都是Python下的应用于「机器学习」的库(框架)。用来处理大量数据。

    GraphLab Creat,是Carlos Guestrin教授(此公开课的讲师之一)在2009年写的一个开源框架,最初用于机器学习,后被广泛用于数据挖掘(data-mining)

    Carlos Guestrin教授,在2013年创建了一间叫Turi的公司,以继续GraphLab项目。这间公司在2016年8月5日被Apple以2亿美金收购。

    安装

    环境安装,不在此赘述,可以参考官网:Install GraphLab Create(需要先注册,下载使用。免费使用一年)

    也在在线使用:Install GraphLab Create on AWS for Coursera Students

    基本操作:

    iPython Notebook的编程操作界面
    • 启动GraphLab Create:import graphlab

    这样,就可以使用GraphLab Creat中的所有工具了,包括SFrame和将要用到的算法。

    快捷键shift + enter 就可以跳到下一行(新建一行)。

    • 加载数据,支持多种格式的数据,CSV(逗号分隔文件):sf = graphlab.SFrame('people-example.csv')

    备注:people-example.csv文件要放在同一目录下(people-example.csv文件,就是一个表格类型的数据文件)。

    • 显示数据:sf 或者sf.head()。直接输入变量名,接着敲Shift+回车,这样就会显示前面几行了(一个表格)。显示后面几行数据:sf.tail()
    • 可视化数据:直接用show()函数,比如sf.show()即可用图表方式查看数据。这种情况下,图表会显示在一个新的页面上。

    如果想直接iPython Notebook中看到可视化的数据,需要进行如下设置graphlab.canvas.set_target('ipynb') 这样就可以在当前页面看到可视化的数据了,无需跳转到其他页面。

    sf['age'].show(view = 'Categorical'),表示显示「age」这一列,并以「分类排序(Categorical)」的形式显示。

    • 检索数据

    • 查看某一列的数据sf['Country']

    • 计算某一列的平均值sf['age'].mean()

    • 查看某列的最大值sf['age'].max()

    • 创建新的一列:sf['Full Name'] = sf['First Name'] + '' + sf['Last Name']。「Full Name」是新建列的名称,等号后面是新建列的内容。

    在机器学习中,经常要将一些列进行转换,建成新的一列,这个过程叫做「feature engineering」。上面代码,就增加了一个「Full Name」特征。

    • 一些运算

    • sf['age'] + 2:age列年龄都加2

    • sf['age'] * sf['age']:age列年龄相乘

    • Apply()函数,让你不用for循环,就能改变每一行(需要改变的)。(实现:将Country列中的所有USA改为United States)

    先编写函数

    def transform_country(country):
       if country == 'USA'
           return 'United States'
       else:
           return country
    

    应用函数,并赋值给Country列(这样,每一行的USA就都会转换为United States了)

    sf['Country'] = sf['Country'].apply(transform_country)

    案例一:regression, linear regression(回归、线性回归)

    这个案例,和吴恩达课程中的一样——预测房价。

    假设我们有一组房子的数据,包括房子面积、房价、房间个数、洗手间个数等属性。如何通过这些数据,预测未知房子的房价?

    如何实现?

    正常情况下,我们想到利用一个坐标(Y轴是售价,X轴是房子面积),然后把所有已知数据标上去,然后「输入」你要预测房子的面积,你就会在Y轴得到一个大概的价格。如下图:

    利用坐标轴预测房价

    以上这种预测是简单、原始、不精确的。这时我们聪明的科学家想到了另一种解决问题的途径——利用统计学常用的分析数据的方法——「线性回归」,利用方程式,得到一条线,这样会得到更为精确的结果。如下图:

    利用统计学的「线性回归」预测房价

    当然,这里问题就会变成:如何找到(「拟合」出)哪条最为精确的线?其中会涉及「二次方程」、「Residual sum of squares(RSS)(残差平方和/最小平方法)」等等概念,我们这里暂且忽略,有兴趣的可看课程详细了解。

    当我们努力得出一条线(或者叫构造了一个(预测房价的)模型),如何确认这个模型的准确性呢?

    这时候我们会把上面提到的那组真实的房价数据,划分为两部分,一个叫训练集(training set),一个叫测试集test set。训练集用于训练模型,测试集用于评价模型的准确性,并根据测试结果,按需调整模型的参数,使模型更准确。

    将已有的房价数据,分为「训练集」和「测试集」

    具体到代码:

    • 将数据随机分为训练集和测试集:

    train_data, test_data = sales.random_split(.8, seed = 0)

    • 利用训练集构建(训练)模型:
      predict_house_price_model = graphlab.linear_regression.create(train_data, target = 'price', features = ['sqft_living'])

    可以看到,利用GraphLab中linear_regression的create()函数,输入训练集,即可构建预测房价的模型。非常方便。

    另外,最后一个参数features,传入的是一个数组,而现在,这个数组只有一个元素,就是房子的面积。为了获取更精确的预测结果,我们可以加入更多的特征,比如房子的房间数量,洗手间数量(课程中也有构建一个多特征的预测模型,并比较了两个模型的误差)。

    • 评估模型的准确性:

    predict_house_price_model.evaluate(test_data)

    利用evaluate()函数,输入test_data作为参数,即可评估模型的准确性。

    • 预测某个房子的房价

    predict_house_price_model.predict(house1)

    所以,总结一下,把机器学习应用到房价预测中的流程:

    • 拿到一组数据,并将其划分为「训练集」和「测试集」;
    • 利用「线性回归」方法,传入「训练集」构建一个预测房价的模型(俗称「喂数据」);
    • 利用「测试集」,评估模型的准确性(误差)。并根据准确性,对模型进行必要的调整;
    • 应用模型预测房价。

    示意图如下:

    机器学习,令程序更「聪明」

    案例二:Classification(分类)

    案例二需要解决的问题是:如何从购物评论中,判断这个评价是好评还是差评?

    课程中,利用分类(Classification)来解决此类问题,示意图如下:

    利用「分类」解决问题

    另外,「分类」可以判断多种类型(比如判断一篇文章是关于「体育」的,「金融」的,还是「科技」的)。

    「分类」的其他应用领域:判断邮件是否为垃圾邮件;判断图片属于哪类型——其实就是我们熟知的图片识别了(后面会介绍到);判断一个人的健康状况……

    Decision Boundary/决策边界

    因为「分类」问题的数据,是离散的(区别于预测房价的数据,是线形的),所以会有一个Decision Boundary/决策边界的术语,如下示意图:

    Boundary/决策边界

    你可以理解为,决策边界上的评论,不是差评,也不是好评。

    因此,我们也把这种「分类器」,成为「线性分类器(Linear calssifiers)」

    看看代码:

    • 将数据随机分为训练集和测试集:

    train_data,test_data = products.random_split(.8, seed=0)

    • 利用训练集构建(训练)模型:
    sentiment_model = graphlab.logistic_classifier.create(train_data,
                                                        target='sentiment',
                                                      features=['word_count'],
                                                validation_set=test_data)
    
    
    • 评估模型:

    sentiment_model.evaluate(test_data, metric='roc_curve')

    • 应用模型:

    sentiment_model.predict(giraffe_reviews, output_type='probability')

    通过代码可以看出,应用机器学习解决分类问题,和案例一中是相仿的,都是利用「训练集」「喂」数据给模型,得出预测模型,并对其进行调整,以提高其精确性;最后再应用模型解决实际问题。

    案例三:Clustering(聚类)

    这个案例要解决的问题是:查找相似的文章(想象你在手机看着一篇自己喜欢的文章,计算机要找相似的文章给你)。所以,问题就变成:检索文章的相似度。

    而本质上,这其实是一个多元化分类问题(Multiclass classification problem)(课程介绍「分类」时有提及)。

    我们要把若干文章进行分组——科学家用一个术语表示这个过程——「聚类(Clustering)」。(分为体育新闻、世界新闻、娱乐新闻等等几大类)

    而区别于上面讲的「线性回归」,「Clustering(聚类)」是一个「Unsupervised learning(无监督学习)」,因为我们的运算不需要任何(给定的)标签。

    输入:这里的Training Data是文档,文档id,文档文本等这些东西。

    输出:模型输出的是聚类标签(cluster label)

    但是没有Test Data验证模型的准确性。所以依靠的是一个叫做「Voronoi tessellation/沃罗诺伊图」这样一个东西来做评估。

    示意图:

    Clustering(聚类)

    案例会引出「词袋模型(Bag of words model)」、「tf-idf」、「最邻近搜索(Nearest neighbor search)」等等这些术语,都是应用于检索文章相似度的,有兴趣可以看课程详细章节。

    看看代码:

    • 为整个文集people增加一列word_count

    people['word_count'] = graphlab.text_analytics.count_words(people['text'])

    • 利用tf_idf()函数,得出tf_idf值

    (暂时将tf_idf理解为一个「可以增加生僻词、关键词的权重,使评估文档相似性更加准确」的算法)

    tfidf = graphlab.text_analytics.tf_idf(people['word_count'])

    • 构建Nearest neighbor search模型

    (Nearest neighbor search算法的Output返回的是一个集合,包含最相似的文章)

    knn_model = graphlab.nearest_neighbors.create(people, features = ['tfidf'], label = 'name')

    • 应用模型(检索到相似的文章)

    knn_model.query(obama)

    返回的结果类似如下:

    与Barack Obama这篇文章「距离」越近的,表示文章相似度越高。

    reference_label distance rank
    Barack Obama 0.0 1
    Joe Biden 0.794117647059 2
    Joe Lieberman 0.794685990338 3
    Kelly Ayotte 0.811989100817 4
    Bill Clinton 0.813852813853 5

    案例四:Collaborative Fitering(协同过滤)

    这个案例要解决的问题是:制作一个商品推荐系统,推荐使用者需要的商品。

    课程给出了几种解决方案:

    • Solution 0: Popularity

    最简单的解决方案:市面上流行什么,就推荐什么。

    缺点:不够个性化。

    • Solution 1: Classification model

    「分类」,案例二介绍过的。

    利用用户信息、购物历史、商品信息等其他信息作为参数,做一个二元「分类器(Classifier)」,判断购物者是否会想购买此商品。

    优点:可以做到个性化;可以将购物情景(节日等)作为特征考虑进去……

    缺点:特征可能不可用。

    • Solution 2: People who bought this also bought...

    这个方案,利用一种叫做「协同过滤/Collaborative filtering」的方法为用户做推荐,但前提是:有其他人的历史购物记录、产品推荐实例、用户和商品的一般化关系。

    这个方法,引出了「协同矩阵/co-occurrence matrix」:保存了人们一同购买的产品的信息。

    缺点:没有利用到用户特征、商品特征;「冷启动(Cold start problem)问题」——就是一个新注册用户,或者上架了一件新商品,没办法做推荐。

    • Solution 3: Discovering hidden structure by matrix factorization

    这种方案,引入「矩阵因子分解(Matrix factorization)」这个方法,是一种推理方式。通过把这个矩阵因式分解的方式来逼近它自身。

    但是「Matrix factorization」还是没办法解决「Cold start problem」这个问题

    所以,将各种模型结合起来提高性能的方法,能超越任何一种单一模型所能达到的性能,这是一种常见并且十分有效的技术。

    看看代码(构建歌曲推荐系统):

    • 将数据随机分为训练集和测试集:

    train_data,test_data = song_data.random_split(.8,seed=0)

    • 利用训练集构建一个简单的推荐模型(根据流程程度来推荐)
    popularity_model = graphlab.popularity_recommender.create(train_data,
                                                            user_id='user_id',
                                                            item_id='song')
    
    
    • 利用训练集构建一个更具个性化的推荐模型
    personalized_model = graphlab.item_similarity_recommender.create(train_data,
                                                                   user_id='user_id',
                                                                   item_id='song')
    
    
    • 应用模型

    personalized_model.recommend(users=[users[0]])

    • 比较两个模型的表现
    if graphlab.version[:3] >= "1.6":
       model_performance = graphlab.compare(test_data, [popularity_model, personalized_model], user_sample=0.05)
       graphlab.show_comparison(model_performance,[popularity_model, personalized_model])
    else:
       %matplotlib inline
       model_performance = graphlab.recommender.util.compare_models(test_data, [popularity_model, personalized_model], user_sample=.05)
    
    

    评估推荐模型的性能,用一个叫做「precision recall curve(精度-召回率曲线)」来评价(横轴是recall,纵轴是precision)。

    曲线和横纵坐标围成的区域的面积越大,表现越好。如下图:

    评估推荐系统的precision recall curve

    可以看到,personalized_model的表现是比popularity_model表现好的。

    案例五:Deep learning(深度学习)

    这个案例要解决的问题是:图像识别——具体是「基于展示照片的相似度选购商品」。

    上面「主要概念」已经介绍过,有些问题利用传统的机器学习无法解决(课程中的「XOR」问题做引子),换言之就是不能直接从「线性」的「特征」得出问题的解决方案。需要用到「非线性的数据表现形式/non-linear features」,而「神经网络」,提供了一种非线性的数据表现形式/non-linear features。(示意图见上面「主要概念介绍」)

    第一个让神经网络大显身手的领域:计算机视觉——分析图像和视频。

    计算机视觉中,「image feature」相当于「local detector/局部探测器」,这些「探测器」结合起来就能做出预测(图像识别)

    例子:识别一张人脸:鼻子探测器、左眼探测器、右眼探测器、嘴巴探测器……如果所有探测器都探测到对应的东西,就可以预测这是一张人脸。

    但是实际中是没有「鼻子探测器」这些东西的。实际是应用「image feature」:collections of locally interesting points——Combined to bulid classifiers。就是一些局部特征探测器/detectors of local features。

    在以前,这些局部特征探测器都是手工完成的(SIFT features)。

    而「神经网络」方法,可以自动去发现和识别这些特征。「神经网络」在不同的层里,抓取图像特征,做到自动学习。(深度学习令人振奋之处,就是它能从图片中学习一些非常复杂的特征——识别德国交通信号灯准确率:99.5%;识别谷歌那些门牌号数字准确率:97.8%)。

    「神经网络」的局限:

    • 需要大量精确的数据
    • 计算代价昂贵
    • 很难调整:因为太多层,太多参数,太太太复杂。(所以如果仓促把过多的选择和计算开销搅在一起,会很难搞清楚,到低哪个「神经网络」比较适用。)

    为解决这一问题,引进「深度特征/deep features」:能够帮助我们建立「神经网络」——甚至你没有很多数据的时候。

    Deep features = Deep learning + Transfer learning(迁移学习)

    「深度特征」提供了一个很好的途径,让我们在仅有很少数据的情况下,构建高准确率的预测模型(分类器),如下示意图:(代码实践中,图像识别的准确率,由47%提高到78%)

    利用「深度特征」(迁移学习)提高图像识别准确率

    看看代码:

    • 加载图片数据,将数据随为训练集和测试集:
    image_train = graphlab.SFrame('image_train_data/')
    image_test = graphlab.SFrame('image_test_data/')
    
    
    • 利用训练集,用「原始的照片像素」构建一个分类器模型(识别图像):

    注意:「feature」是「图片像素」,并没有用到「deep feature」

    # 在sentiment analysis中有用过「logistic_classifier()」函数
    # create:代表创造模型
    # 参数1:训练集
    # 参数2:目标——数据集中label那一列
    # 参数3:会用到哪些特征(image_array保存的是数据的元素像素)
    raw_piexl_model = graphlab.logistic_classifier.create(image_train, target = 'label',
                                                        features = ['image_array'])
    # 上面的代码表示:用原始的照片像素建立一个分类器
    
    
    • 应用模型识别图片:
    # 利用原始像素模型预测前三张图片(结果是三张都识别错了)
    raw_piexl_model.predict(image_test[0:3])
    
    
    • 评估模型识别图片的准确率:
    # 调用evaluate()函数评估模型的准确率
    raw_piexl_model.evaluate(image_test)
    
    

    运行后,可以看到'accuracy': 0.47825,,只有48%左右的准确率,很不理想

    • 提取「深度特征(Deep features)」
    # 用graphlab的load_model()函数,载入一个deep_learning_model,这个是已经训练好/pre-trained的了(载入即可)。
    # 「imagenet_model」是模型载入的数据,有150张照片,1000个标签。
    deep_learning_model = graphlab.load_model('imagenet_model')
    
    # 在image_train中添加一列deep_features;
    # extract_features()函数用来提取「深度特征/deep features」,传入的参数,就是将提取出来的特征,要用到哪个数据集中。
    image_train['deep_features'] = deep_learning_model.extract_features(image_train)
    
    

    以上两行代码,就是用来做「迁移学习/transfer learning」的(用网上训练的特征,应用到image_train数据集中)

    • 利用训练集,用「深度特征」构建(训练)模型:
    # 构建另一个有别于基于像素进行预测的模型deep_features_model
    # 也是用logistic_classifier的create()函数构建
    # 参数1:训练集
    # 参数2:会用到哪些特征(deep_features是刚刚通过「迁移学习」得到的)
    # 参数3:目标——数据集中label那一列
    deep_features_model = graphlab.logistic_classifier.create(image_train,
                                                            features = ['deep_features'],
                                                            target = 'label')
    
    
    • 应用deep_features_model识别图片(3张都识别正确了):

    deep_features_model.predict(image_test[0:3])

    并且准确率提高到'accuracy': 0.7765,,78%左右。

    篇幅有限,此案例要解决的问题——「基于展示照片的相似度选购商品」,思路就是利用「最邻近搜索(Nearest neighbor search)」算法(「聚类」中有涉及)、「深度特征」构建模型,再利用这个模型输入图片,就会输出类似的图片。代码可参考:tjaskula/Coursera

    学识有限,课程中有很多概念、方法也还没有消化,谬误在所难免,也不求你斧正了。权当科普文一览。

    相关文章

      网友评论

        本文标题:文科汪入门「机器学习」

        本文链接:https://www.haomeiwen.com/subject/fbfpcxtx.html