精华内容
下载资源
问答
  • 机器学习系列(3)_逻辑回归应用之Kaggle泰坦尼克之灾

    万次阅读 多人点赞 2015-11-12 12:07:12
    手把手机器学习之逻辑回归应用——Kaggle泰坦尼克之灾1.引言先说一句,年末双十一什么的一来,真是非(mang)常(cheng)欢(gou)乐(le)!然后push自己抽出时间来写这篇blog的原因也非常简单: 写完前两篇逻辑回归的介绍...

    作者: 寒小阳
    时间:2015年11月。
    出处:http://blog.csdn.net/han_xiaoyang/article/details/49797143
    声明:版权所有,转载请注明出处,谢谢。

    1.引言

    先说一句,年末双十一什么的一来,真是非(mang)常(cheng)欢(gou)乐(le)!然后push自己抽出时间来写这篇blog的原因也非常简单:

    • 写完前两篇逻辑回归的介绍和各个角度理解之后,我们讨论群(戳我入群)的小伙伴们纷纷表示『好像很高级的样纸,but 然并卵 啊!你们倒是拿点实际数据来给我们看看,这玩意儿 有!什!么!用!啊!』
    • talk is cheap, show me the code!
    • no example say a jb!

    OK,OK,这就来了咯,同学们别着急,我们先找个简单的实际例子,来看看,所谓的数据挖掘或者机器学习实际应用到底是怎么样一个过程。

    『喂,那几个说要看大数据上机器学习应用的,对,就是说你们!别着急好么,我们之后拉点大一点实际数据用liblinear或者spark,MLlib跑给你们看,行不行?咱们先拿个实例入入门嘛』

    好了,我是一个严肃的技术研究和分享者,咳咳,不能废话了,各位同学继续往下看吧!

    2.背景

    2.1 关于Kaggle

    • 我是Kaggle地址,翻我牌子
    • 亲,逼格这么高的地方,你一定听过对不对?是!这就是那个无数『数据挖掘先驱』们,在回答"枪我有了,哪能找到靶子练练手啊?"时候的答案!
    • 这是一个要数据有数据,要实际应用场景有场景,要一起在数据挖掘领域high得不要不要的小伙伴就有小伙伴的地方啊!!!

    艾玛,逗逼模式开太猛了。恩,不闹,不闹,说正事,Kaggle是一个数据分析建模的应用竞赛平台,有点类似KDD-CUP(国际知识发现和数据挖掘竞赛),企业或者研究者可以将问题背景、数据、期望指标等发布到Kaggle上,以竞赛的形式向广大的数据科学家征集解决方案。而热爱数(dong)据(shou)挖(zhe)掘(teng)的小伙伴们可以下载/分析数据,使用统计/机器学习/数据挖掘等知识,建立算法模型,得出结果并提交,排名top的可能会有奖金哦!

    2.2 关于泰坦尼克号之灾

    • 带大家去该问题页面溜达一圈吧

      • 下面是问题背景页
        泰坦尼克号问题背景页
      • 下面是可下载Data的页面
        Data页面
      • 下面是小伙伴们最爱的forum页面,你会看到各种神级人物厉(qi)害(pa)的数据处理/建模想法,你会直视『世界真奇妙』。
        论坛页面
    • 泰坦尼克号问题之背景

      • 就是那个大家都熟悉的『Jack and Rose』的故事,豪华游艇倒了,大家都惊恐逃生,可是救生艇的数量有限,无法人人都有,副船长发话了『lady and kid first!』,所以是否获救其实并非随机,而是基于一些背景有rank先后的

      • 训练和测试数据是一些乘客的个人信息以及存活状况,要尝试根据它生成合适的模型并预测其他人的存活状况

      • 对,这是一个二分类问题,是我们之前讨论的logistic regression所能处理的范畴。

    3.说明

    接触过Kaggle的同学们可能知道这个问题,也可能知道RandomForest和SVM等等算法,甚至还对多个模型做过融合,取得过非常好的结果,那maybe这篇文章并不是针对你的,你可以自行略过。

    我们因为之前只介绍了Logistic Regression这一种分类算法。所以本次的问题解决过程和优化思路,都集中在这种算法上。其余的方法可能我们之后的文章里会提到。

    说点个人的观点。不一定正确。
    『解决一个问题的方法和思路不止一种』
    『没有所谓的机器学习算法优劣,也没有绝对高性能的机器学习算法,只有在特定的场景、数据和特征下更合适的机器学习算法。』

    4.怎么做?

    手把手教程马上就来,先来两条我看到的,觉得很重要的经验。

    1. 印象中Andrew Ng老师似乎在coursera上说过,应用机器学习,千万不要一上来就试图做到完美,先撸一个baseline的model出来,再进行后续的分析步骤,一步步提高,所谓后续步骤可能包括『分析model现在的状态(欠/过拟合),分析我们使用的feature的作用大小,进行feature selection,以及我们模型下的bad case和产生的原因』等等。

    2. Kaggle上的大神们,也分享过一些experience,说几条我记得的哈:

      • 『对数据的认识太重要了!』
      • 『数据中的特殊点/离群点的分析和处理太重要了!』
      • 『特征工程(feature engineering)太重要了!在很多Kaggle的场景下,甚至比model本身还要重要』
      • 『要做模型融合(model ensemble)啊啊啊!』

    更多的经验分享请加讨论群,具体方式请联系作者,或者参见《“ML学分计划”说明书》

    5.初探数据

    先看看我们的数据,长什么样吧。在Data下我们train.csv和test.csv两个文件,分别存着官方给的训练和测试数据。

    import pandas as pd #数据分析
    import numpy as np #科学计算
    from pandas import Series,DataFrame
    
    data_train = pd.read_csv("/Users/Hanxiaoyang/Titanic_data/Train.csv")
    data_train
    

    pandas是常用的python数据处理包,把csv文件读入成dataframe各式,我们在ipython notebook中,看到data_train如下所示:

    训练数据

    这就是典型的dataframe格式,如果你没接触过这种格式,完全没有关系,你就把它想象成Excel里面的列好了。
    我们看到,总共有12列,其中Survived字段表示的是该乘客是否获救,其余都是乘客的个人信息,包括:

    • PassengerId => 乘客ID
    • Pclass => 乘客等级(1/2/3等舱位)
    • Name => 乘客姓名
    • Sex => 性别
    • Age => 年龄
    • SibSp => 堂兄弟/妹个数
    • Parch => 父母与小孩个数
    • Ticket => 船票信息
    • Fare => 票价
    • Cabin => 客舱
    • Embarked => 登船港口

    逐条往下看,要看完这么多条,眼睛都有一种要瞎的赶脚。好吧,我们让dataframe自己告诉我们一些信息,如下所示:

    data_train.info()
    

    看到了如下的信息:
    数据信息

    上面的数据说啥了?它告诉我们,训练数据中总共有891名乘客,但是很不幸,我们有些属性的数据不全,比如说:

    • Age(年龄)属性只有714名乘客有记录
    • Cabin(客舱)更是只有204名乘客是已知的

    似乎信息略少啊,想再瞄一眼具体数据数值情况呢?恩,我们用下列的方法,得到数值型数据的一些分布(因为有些属性,比如姓名,是文本型;而另外一些属性,比如登船港口,是类目型。这些我们用下面的函数是看不到的):

    数值型数据基本信息

    我们从上面看到更进一步的什么信息呢?
    mean字段告诉我们,大概0.383838的人最后获救了,2/3等舱的人数比1等舱要多,平均乘客年龄大概是29.7岁(计算这个时候会略掉无记录的)等等…

    6.数据初步分析

    每个乘客都这么多属性,那我们咋知道哪些属性更有用,而又应该怎么用它们啊?说实话这会儿我也不知道,但我们记得前面提到过

    • 『对数据的认识太重要了!』
    • 『对数据的认识太重要了!』
    • 『对数据的认识太重要了!』

    重要的事情说三遍,恩,说完了。仅仅最上面的对数据了解,依旧无法给我们提供想法和思路。我们再深入一点来看看我们的数据,看看每个/多个 属性和最后的Survived之间有着什么样的关系呢。

    6.1 乘客各属性分布

    脑容量太有限了…数值看花眼了。我们还是统计统计,画些图来看看属性和结果之间的关系好了,代码如下:

    import matplotlib.pyplot as plt
    fig = plt.figure()
    fig.set(alpha=0.2)  # 设定图表颜色alpha参数
    
    plt.subplot2grid((2,3),(0,0))             # 在一张大图里分列几个小图
    data_train.Survived.value_counts().plot(kind='bar')# 柱状图 
    plt.title(u"获救情况 (1为获救)") # 标题
    plt.ylabel(u"人数")  
    
    plt.subplot2grid((2,3),(0,1))
    data_train.Pclass.value_counts().plot(kind="bar")
    plt.ylabel(u"人数")
    plt.title(u"乘客等级分布")
    
    plt.subplot2grid((2,3),(0,2))
    plt.scatter(data_train.Survived, data_train.Age)
    plt.ylabel(u"年龄")                         # 设定纵坐标名称
    plt.grid(b=True, which='major', axis='y') 
    plt.title(u"按年龄看获救分布 (1为获救)")
    
    
    plt.subplot2grid((2,3),(1,0), colspan=2)
    data_train.Age[data_train.Pclass == 1].plot(kind='kde')   
    data_train.Age[data_train.Pclass == 2].plot(kind='kde')
    data_train.Age[data_train.Pclass == 3].plot(kind='kde')
    plt.xlabel(u"年龄")# plots an axis lable
    plt.ylabel(u"密度") 
    plt.title(u"各等级的乘客年龄分布")
    plt.legend((u'头等舱', u'2等舱',u'3等舱'),loc='best') # sets our legend for our graph.
    
    
    plt.subplot2grid((2,3),(1,2))
    data_train.Embarked.value_counts().plot(kind='bar')
    plt.title(u"各登船口岸上船人数")
    plt.ylabel(u"人数")  
    plt.show()
    

    数据基本信息图示

    bingo,图还是比数字好看多了。所以我们在图上可以看出来,被救的人300多点,不到半数;3等舱乘客灰常多;遇难和获救的人年龄似乎跨度都很广;3个不同的舱年龄总体趋势似乎也一致,2/3等舱乘客20岁多点的人最多,1等舱40岁左右的最多(→_→似乎符合财富和年龄的分配哈,咳咳,别理我,我瞎扯的);登船港口人数按照S、C、Q递减,而且S远多于另外俩港口。

    这个时候我们可能会有一些想法了:

    • 不同舱位/乘客等级可能和财富/地位有关系,最后获救概率可能会不一样
    • 年龄对获救概率也一定是有影响的,毕竟前面说了,副船长还说『小孩和女士先走』呢
    • 和登船港口是不是有关系呢?也许登船港口不同,人的出身地位不同?

    口说无凭,空想无益。老老实实再来统计统计,看看这些属性值的统计分布吧。

    6.2 属性与获救结果的关联统计

    #看看各乘客等级的获救情况
    fig = plt.figure()
    fig.set(alpha=0.2)  # 设定图表颜色alpha参数
    
    Survived_0 = data_train.Pclass[data_train.Survived == 0].value_counts()
    Survived_1 = data_train.Pclass[data_train.Survived == 1].value_counts()
    df=pd.DataFrame({u'获救':Survived_1, u'未获救':Survived_0})
    df.plot(kind='bar', stacked=True)
    plt.title(u"各乘客等级的获救情况")
    plt.xlabel(u"乘客等级") 
    plt.ylabel(u"人数") 
    plt.show()
    

    各乘客等级的获救情况

    啧啧,果然,钱和地位对舱位有影响,进而对获救的可能性也有影响啊←_←
    咳咳,跑题了,我想说的是,明显等级为1的乘客,获救的概率高很多。恩,这个一定是影响最后获救结果的一个特征。

    #看看各性别的获救情况
    fig = plt.figure()
    fig.set(alpha=0.2)  # 设定图表颜色alpha参数
    
    Survived_m = data_train.Survived[data_train.Sex == 'male'].value_counts()
    Survived_f = data_train.Survived[data_train.Sex == 'female'].value_counts()
    df=pd.DataFrame({u'男性':Survived_m, u'女性':Survived_f})
    df.plot(kind='bar', stacked=True)
    plt.title(u"按性别看获救情况")
    plt.xlabel(u"性别") 
    plt.ylabel(u"人数")
    plt.show()
    

    各乘客等级的获救情况

    歪果盆友果然很尊重lady,lady first践行得不错。性别无疑也要作为重要特征加入最后的模型之中。

    再来个详细版的好了。

    
     #然后我们再来看看各种舱级别情况下各性别的获救情况
    fig=plt.figure()
    fig.set(alpha=0.65) # 设置图像透明度,无所谓
    plt.title(u"根据舱等级和性别的获救情况")
    
    ax1=fig.add_subplot(141)
    data_train.Survived[data_train.Sex == 'female'][data_train.Pclass != 3].value_counts().plot(kind='bar', label="female highclass", color='#FA2479')
    ax1.set_xticklabels([u"获救", u"未获救"], rotation=0)
    ax1.legend([u"女性/高级舱"], loc='best')
    
    ax2=fig.add_subplot(142, sharey=ax1)
    data_train.Survived[data_train.Sex == 'female'][data_train.Pclass == 3].value_counts().plot(kind='bar', label='female, low class', color='pink')
    ax2.set_xticklabels([u"未获救", u"获救"], rotation=0)
    plt.legend([u"女性/低级舱"], loc='best')
    
    ax3=fig.add_subplot(143, sharey=ax1)
    data_train.Survived[data_train.Sex == 'male'][data_train.Pclass != 3].value_counts().plot(kind='bar', label='male, high class',color='lightblue')
    ax3.set_xticklabels([u"未获救", u"获救"], rotation=0)
    plt.legend([u"男性/高级舱"], loc='best')
    
    ax4=fig.add_subplot(144, sharey=ax1)
    data_train.Survived[data_train.Sex == 'male'][data_train.Pclass == 3].value_counts().plot(kind='bar', label='male low class', color='steelblue')
    ax4.set_xticklabels([u"未获救", u"获救"], rotation=0)
    plt.legend([u"男性/低级舱"], loc='best')
    
    plt.show()
    
    

    各性别和舱位的获救情况

    恩,坚定了之前的判断。

    我们看看各登船港口的获救情况。

    fig = plt.figure()
    fig.set(alpha=0.2)  # 设定图表颜色alpha参数
    
    Survived_0 = data_train.Embarked[data_train.Survived == 0].value_counts()
    Survived_1 = data_train.Embarked[data_train.Survived == 1].value_counts()
    df=pd.DataFrame({u'获救':Survived_1, u'未获救':Survived_0})
    df.plot(kind='bar', stacked=True)
    plt.title(u"各登录港口乘客的获救情况")
    plt.xlabel(u"登录港口") 
    plt.ylabel(u"人数") 
    
    plt.show()
    

    各登船港口的获救情况

    下面我们来看看 堂兄弟/妹,孩子/父母有几人,对是否获救的影响。

    
    g = data_train.groupby(['SibSp','Survived'])
    df = pd.DataFrame(g.count()['PassengerId'])
    print df
    
    g = data_train.groupby(['SibSp','Survived'])
    df = pd.DataFrame(g.count()['PassengerId'])
    print df
    
    

    堂兄弟/妹影响

    父母/孩子影响

    好吧,没看出特别特别明显的规律(为自己的智商感到捉急…),先作为备选特征,放一放。

    
    #ticket是船票编号,应该是unique的,和最后的结果没有太大的关系,先不纳入考虑的特征范畴把
    #cabin只有204个乘客有值,我们先看看它的一个分布
    data_train.Cabin.value_counts()
    
    

    部分结果如下:
    Cabin分布

    这三三两两的…如此不集中…我们猜一下,也许,前面的ABCDE是指的甲板位置、然后编号是房间号?…好吧,我瞎说的,别当真…

    关键是Cabin这鬼属性,应该算作类目型的,本来缺失值就多,还如此不集中,注定是个棘手货…第一感觉,这玩意儿如果直接按照类目特征处理的话,太散了,估计每个因子化后的特征都拿不到什么权重。加上有那么多缺失值,要不我们先把Cabin缺失与否作为条件(虽然这部分信息缺失可能并非未登记,maybe只是丢失了而已,所以这样做未必妥当),先在有无Cabin信息这个粗粒度上看看Survived的情况好了。

    
    fig = plt.figure()
    fig.set(alpha=0.2)  # 设定图表颜色alpha参数
    
    Survived_cabin = data_train.Survived[pd.notnull(data_train.Cabin)].value_counts()
    Survived_nocabin = data_train.Survived[pd.isnull(data_train.Cabin)].value_counts()
    df=pd.DataFrame({u'有':Survived_cabin, u'无':Survived_nocabin}).transpose()
    df.plot(kind='bar', stacked=True)
    plt.title(u"按Cabin有无看获救情况")
    plt.xlabel(u"Cabin有无") 
    plt.ylabel(u"人数")
    plt.show()
    
    

    有无Cabin记录影响

    咳咳,有Cabin记录的似乎获救概率稍高一些,先这么着放一放吧。

    7.简单数据预处理

    大体数据的情况看了一遍,对感兴趣的属性也有个大概的了解了。
    下一步干啥?咱们该处理处理这些数据,为机器学习建模做点准备了。

    对了,我这里说的数据预处理,其实就包括了很多Kaggler津津乐道的feature engineering过程,灰常灰常有必要!

    『特征工程(feature engineering)太重要了!』
    『特征工程(feature engineering)太重要了!』
    『特征工程(feature engineering)太重要了!』

    恩,重要的事情说三遍。

    先从最突出的数据属性开始吧,对,Cabin和Age,有丢失数据实在是对下一步工作影响太大。

    先说Cabin,暂时我们就按照刚才说的,按Cabin有无数据,将这个属性处理成Yes和No两种类型吧。

    再说Age:

    通常遇到缺值的情况,我们会有几种常见的处理方式

    • 如果缺值的样本占总数比例极高,我们可能就直接舍弃了,作为特征加入的话,可能反倒带入noise,影响最后的结果了
    • 如果缺值的样本适中,而该属性非连续值特征属性(比如说类目属性),那就把NaN作为一个新类别,加到类别特征中
    • 如果缺值的样本适中,而该属性为连续值特征属性,有时候我们会考虑给定一个step(比如这里的age,我们可以考虑每隔2/3岁为一个步长),然后把它离散化,之后把NaN作为一个type加到属性类目中。
    • 有些情况下,缺失的值个数并不是特别多,那我们也可以试着根据已有的值,拟合一下数据,补充上。

    本例中,后两种处理方式应该都是可行的,我们先试试拟合补全吧(虽然说没有特别多的背景可供我们拟合,这不一定是一个多么好的选择)

    我们这里用scikit-learn中的RandomForest来拟合一下缺失的年龄数据(注:RandomForest是一个用在原始数据中做不同采样,建立多颗DecisionTree,再进行average等等来降低过拟合现象,提高结果的机器学习算法,我们之后会介绍到)

    
    from sklearn.ensemble import RandomForestRegressor
     
    ### 使用 RandomForestClassifier 填补缺失的年龄属性
    def set_missing_ages(df):
        
        # 把已有的数值型特征取出来丢进Random Forest Regressor中
        age_df = df[['Age','Fare', 'Parch', 'SibSp', 'Pclass']]
    
        # 乘客分成已知年龄和未知年龄两部分
        known_age = age_df[age_df.Age.notnull()].as_matrix()
        unknown_age = age_df[age_df.Age.isnull()].as_matrix()
    
        # y即目标年龄
        y = known_age[:, 0]
    
        # X即特征属性值
        X = known_age[:, 1:]
    
        # fit到RandomForestRegressor之中
        rfr = RandomForestRegressor(random_state=0, n_estimators=2000, n_jobs=-1)
        rfr.fit(X, y)
        
        # 用得到的模型进行未知年龄结果预测
        predictedAges = rfr.predict(unknown_age[:, 1::])
        
        # 用得到的预测结果填补原缺失数据
        df.loc[ (df.Age.isnull()), 'Age' ] = predictedAges 
        
        return df, rfr
    
    def set_Cabin_type(df):
        df.loc[ (df.Cabin.notnull()), 'Cabin' ] = "Yes"
        df.loc[ (df.Cabin.isnull()), 'Cabin' ] = "No"
        return df
    
    data_train, rfr = set_missing_ages(data_train)
    data_train = set_Cabin_type(data_train)
    
    

    处理Cabin和Age之后

    恩。目的达到,OK了。

    因为逻辑回归建模时,需要输入的特征都是数值型特征,我们通常会先对类目型的特征因子化。
    什么叫做因子化呢?举个例子:

    以Cabin为例,原本一个属性维度,因为其取值可以是[‘yes’,‘no’],而将其平展开为’Cabin_yes’,'Cabin_no’两个属性

    • 原本Cabin取值为yes的,在此处的"Cabin_yes"下取值为1,在"Cabin_no"下取值为0
    • 原本Cabin取值为no的,在此处的"Cabin_yes"下取值为0,在"Cabin_no"下取值为1

    我们使用pandas的"get_dummies"来完成这个工作,并拼接在原来的"data_train"之上,如下所示。

    
    dummies_Cabin = pd.get_dummies(data_train['Cabin'], prefix= 'Cabin')
    
    dummies_Embarked = pd.get_dummies(data_train['Embarked'], prefix= 'Embarked')
    
    dummies_Sex = pd.get_dummies(data_train['Sex'], prefix= 'Sex')
    
    dummies_Pclass = pd.get_dummies(data_train['Pclass'], prefix= 'Pclass')
    
    df = pd.concat([data_train, dummies_Cabin, dummies_Embarked, dummies_Sex, dummies_Pclass], axis=1)
    df.drop(['Pclass', 'Name', 'Sex', 'Ticket', 'Cabin', 'Embarked'], axis=1, inplace=True)
    df
    
    

    离散/因子化之后

    bingo,我们很成功地把这些类目属性全都转成0,1的数值属性了。

    这样,看起来,是不是我们需要的属性值都有了,且它们都是数值型属性呢。

    有一种临近结果的宠宠欲动感吧,莫急莫急,我们还得做一些处理,仔细看看Age和Fare两个属性,乘客的数值幅度变化,也忒大了吧!!如果大家了解逻辑回归与梯度下降的话,会知道,各属性值之间scale差距太大,将对收敛速度造成几万点伤害值!甚至不收敛! (╬▔皿▔)…所以我们先用scikit-learn里面的preprocessing模块对这俩货做一个scaling,所谓scaling,其实就是将一些变化幅度较大的特征化到[-1,1]之内。

    import sklearn.preprocessing as preprocessing
    scaler = preprocessing.StandardScaler()
    age_scale_param = scaler.fit(df['Age'])
    df['Age_scaled'] = scaler.fit_transform(df['Age'], age_scale_param)
    fare_scale_param = scaler.fit(df['Fare'])
    df['Fare_scaled'] = scaler.fit_transform(df['Fare'], fare_scale_param)
    df
    

    scaling

    恩,好看多了,万事俱备,只欠建模。马上就要看到成效了,哈哈。我们把需要的属性值抽出来,转成scikit-learn里面LogisticRegression可以处理的格式。

    8.逻辑回归建模

    我们把需要的feature字段取出来,转成numpy格式,使用scikit-learn中的LogisticRegression建模。

    from sklearn import linear_model
    
    # 用正则取出我们要的属性值
    train_df = df.filter(regex='Survived|Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass_.*')
    train_np = train_df.as_matrix()
    
    # y即Survival结果
    y = train_np[:, 0]
    
    # X即特征属性值
    X = train_np[:, 1:]
    
    # fit到RandomForestRegressor之中
    clf = linear_model.LogisticRegression(C=1.0, penalty='l1', tol=1e-6)
    clf.fit(X, y)
        
    clf
    

    good,很顺利,我们得到了一个model,如下:
    modeling

    先淡定!淡定!你以为把test.csv直接丢进model里就能拿到结果啊…骚年,图样图森破啊!我们的"test_data"也要做和"train_data"一样的预处理啊!!

    
    data_test = pd.read_csv("/Users/Hanxiaoyang/Titanic_data/test.csv")
    data_test.loc[ (data_test.Fare.isnull()), 'Fare' ] = 0
    # 接着我们对test_data做和train_data中一致的特征变换
    # 首先用同样的RandomForestRegressor模型填上丢失的年龄
    tmp_df = data_test[['Age','Fare', 'Parch', 'SibSp', 'Pclass']]
    null_age = tmp_df[data_test.Age.isnull()].as_matrix()
    # 根据特征属性X预测年龄并补上
    X = null_age[:, 1:]
    predictedAges = rfr.predict(X)
    data_test.loc[ (data_test.Age.isnull()), 'Age' ] = predictedAges
    
    data_test = set_Cabin_type(data_test)
    dummies_Cabin = pd.get_dummies(data_test['Cabin'], prefix= 'Cabin')
    dummies_Embarked = pd.get_dummies(data_test['Embarked'], prefix= 'Embarked')
    dummies_Sex = pd.get_dummies(data_test['Sex'], prefix= 'Sex')
    dummies_Pclass = pd.get_dummies(data_test['Pclass'], prefix= 'Pclass')
    
    
    df_test = pd.concat([data_test, dummies_Cabin, dummies_Embarked, dummies_Sex, dummies_Pclass], axis=1)
    df_test.drop(['Pclass', 'Name', 'Sex', 'Ticket', 'Cabin', 'Embarked'], axis=1, inplace=True)
    df_test['Age_scaled'] = scaler.fit_transform(df_test['Age'], age_scale_param)
    df_test['Fare_scaled'] = scaler.fit_transform(df_test['Fare'], fare_scale_param)
    df_test
    
    

    modeling

    不错不错,数据很OK,差最后一步了。
    下面就做预测取结果吧!!

    test = df_test.filter(regex='Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass_.*')
    predictions = clf.predict(test)
    result = pd.DataFrame({'PassengerId':data_test['PassengerId'].as_matrix(), 'Survived':predictions.astype(np.int32)})
    result.to_csv("/Users/Hanxiaoyang/Titanic_data/logistic_regression_predictions.csv", index=False)
    

    预测结果

    啧啧,挺好,格式正确,去make a submission啦啦啦!

    在Kaggle的Make a submission页面,提交上结果。如下:
    Kaggle排名

    0.76555,恩,结果还不错。毕竟,这只是我们简单分析处理过后出的一个baseline模型嘛。

    9.逻辑回归系统优化

    9.1 模型系数关联分析

    亲,你以为结果提交上了,就完事了?
    我不会告诉你,这只是万里长征第一步啊(泪牛满面)!!!这才刚撸完baseline model啊!!!还得优化啊!!!

    看过Andrew Ng老师的machine Learning课程的同学们,知道,我们应该分析分析模型现在的状态了,是过/欠拟合?,以确定我们需要更多的特征还是更多数据,或者其他操作。我们有一条很著名的learning curves对吧。

    不过在现在的场景下,先不着急做这个事情,我们这个baseline系统还有些粗糙,先再挖掘挖掘。

    • 首先,Name和Ticket两个属性被我们完整舍弃了(好吧,其实是因为这俩属性,几乎每一条记录都是一个完全不同的值,我们并没有找到很直接的处理方式)。

    • 然后,我们想想,年龄的拟合本身也未必是一件非常靠谱的事情,我们依据其余属性,其实并不能很好地拟合预测出未知的年龄。再一个,以我们的日常经验,小盆友和老人可能得到的照顾会多一些,这样看的话,年龄作为一个连续值,给一个固定的系数,应该和年龄是一个正相关或者负相关,似乎体现不出两头受照顾的实际情况,所以,说不定我们把年龄离散化,按区段分作类别属性会更合适一些。

    上面只是我瞎想的,who knows是不是这么回事呢,老老实实先把得到的model系数和feature关联起来看看。

    pd.DataFrame({"columns":list(train_df.columns)[1:], "coef":list(clf.coef_.T)})
    

    LR模型系数

    首先,大家回去前两篇文章里瞄一眼公式就知道,这些系数为正的特征,和最后结果是一个正相关,反之为负相关。

    我们先看看那些权重绝对值非常大的feature,在我们的模型上:

    • Sex属性,如果是female会极大提高最后获救的概率,而male会很大程度拉低这个概率。
    • Pclass属性,1等舱乘客最后获救的概率会上升,而乘客等级为3会极大地拉低这个概率。
    • 有Cabin值会很大程度拉升最后获救概率(这里似乎能看到了一点端倪,事实上从最上面的有无Cabin记录的Survived分布图上看出,即使有Cabin记录的乘客也有一部分遇难了,估计这个属性上我们挖掘还不够)
    • Age是一个负相关,意味着在我们的模型里,年龄越小,越有获救的优先权(还得回原数据看看这个是否合理
    • 有一个登船港口S会很大程度拉低获救的概率,另外俩港口压根就没啥作用(这个实际上非常奇怪,因为我们从之前的统计图上并没有看到S港口的获救率非常低,所以也许可以考虑把登船港口这个feature去掉试试)。
    • 船票Fare有小幅度的正相关(并不意味着这个feature作用不大,有可能是我们细化的程度还不够,举个例子,说不定我们得对它离散化,再分至各个乘客等级上?)

    噢啦,观察完了,我们现在有一些想法了,但是怎么样才知道,哪些优化的方法是promising的呢?

    因为test.csv里面并没有Survived这个字段(好吧,这是废话,这明明就是我们要预测的结果),我们无法在这份数据上评定我们算法在该场景下的效果…

    而『每做一次调整就make a submission,然后根据结果来判定这次调整的好坏』其实是行不通的…

    9.2 交叉验证

    重点又来了:

    『要做交叉验证(cross validation)!』
    『要做交叉验证(cross validation)!』
    『要做交叉验证(cross validation)!』

    恩,重要的事情说三遍。我们通常情况下,这么做cross validation:把train.csv分成两部分,一部分用于训练我们需要的模型,另外一部分数据上看我们预测算法的效果。

    我们用scikit-learn的cross_validation来帮我们完成小数据集上的这个工作。

    先简单看看cross validation情况下的打分

    from sklearn import cross_validation
    
     #简单看看打分情况
    clf = linear_model.LogisticRegression(C=1.0, penalty='l1', tol=1e-6)
    all_data = df.filter(regex='Survived|Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass_.*')
    X = all_data.as_matrix()[:,1:]
    y = all_data.as_matrix()[:,0]
    print cross_validation.cross_val_score(clf, X, y, cv=5)
    

    结果是下面酱紫的:
    [0.81564246 0.81005587 0.78651685 0.78651685 0.81355932]

    似乎比Kaggle上的结果略高哈,毕竟用的是不是同一份数据集评估的。

    等等,既然我们要做交叉验证,那我们干脆先把交叉验证里面的bad case拿出来看看,看看人眼审核,是否能发现什么蛛丝马迹,是我们忽略了哪些信息,使得这些乘客被判定错了。再把bad case上得到的想法和前头系数分析的合在一起,然后逐个试试。

    下面我们做数据分割,并且在原始数据集上瞄一眼bad case:

    # 分割数据,按照 训练数据:cv数据 = 7:3的比例
    split_train, split_cv = cross_validation.train_test_split(df, test_size=0.3, random_state=0)
    train_df = split_train.filter(regex='Survived|Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass_.*')
    # 生成模型
    clf = linear_model.LogisticRegression(C=1.0, penalty='l1', tol=1e-6)
    clf.fit(train_df.as_matrix()[:,1:], train_df.as_matrix()[:,0])
    
    # 对cross validation数据进行预测
    
    cv_df = split_cv.filter(regex='Survived|Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass_.*')
    predictions = clf.predict(cv_df.as_matrix()[:,1:])
    
    origin_data_train = pd.read_csv("/Users/HanXiaoyang/Titanic_data/Train.csv")
    bad_cases = origin_data_train.loc[origin_data_train['PassengerId'].isin(split_cv[predictions != cv_df.as_matrix()[:,0]]['PassengerId'].values)]
    bad_cases
    

    我们判定错误的 bad case 中部分数据如下:
    预测错误的原始数据

    大家可以自己跑一遍试试,拿到bad cases之后,仔细看看。也会有一些猜测和想法。其中会有一部分可能会印证在系数分析部分的猜测,那这些优化的想法优先级可以放高一些。

    现在有了"train_df" 和 “vc_df” 两个数据部分,前者用于训练model,后者用于评定和选择模型。可以开始可劲折腾了。

    我们随便列一些可能可以做的优化操作:

    • Age属性不使用现在的拟合方式,而是根据名称中的『Mr』『Mrs』『Miss』等的平均值进行填充。
    • Age不做成一个连续值属性,而是使用一个步长进行离散化,变成离散的类目feature。
    • Cabin再细化一些,对于有记录的Cabin属性,我们将其分为前面的字母部分(我猜是位置和船层之类的信息) 和 后面的数字部分(应该是房间号,有意思的事情是,如果你仔细看看原始数据,你会发现,这个值大的情况下,似乎获救的可能性高一些)。
    • Pclass和Sex俩太重要了,我们试着用它们去组出一个组合属性来试试,这也是另外一种程度的细化。
    • 单加一个Child字段,Age<=12的,设为1,其余为0(你去看看数据,确实小盆友优先程度很高啊)
    • 如果名字里面有『Mrs』,而Parch>1的,我们猜测她可能是一个母亲,应该获救的概率也会提高,因此可以多加一个Mother字段,此种情况下设为1,其余情况下设为0
    • 登船港口可以考虑先去掉试试(Q和C本来就没权重,S有点诡异)
    • 把堂兄弟/兄妹 和 Parch 还有自己 个数加在一起组一个Family_size字段(考虑到大家族可能对最后的结果有影响)
    • Name是一个我们一直没有触碰的属性,我们可以做一些简单的处理,比如说男性中带某些字眼的(‘Capt’, ‘Don’, ‘Major’, ‘Sir’)可以统一到一个Title,女性也一样。

    大家接着往下挖掘,可能还可以想到更多可以细挖的部分。我这里先列这些了,然后我们可以使用手头上的"train_df"和"cv_df"开始试验这些feature engineering的tricks是否有效了。

    试验的过程比较漫长,也需要有耐心,而且我们经常会面临很尴尬的状况,就是我们灵光一闪,想到一个feature,然后坚信它一定有效,结果试验下来,效果还不如试验之前的结果。恩,需要坚持和耐心,以及不断的挖掘。

    我最好的结果是在『Survived~C(Pclass)+C(Title)+C(Sex)+C(Age_bucket)+C(Cabin_num_bucket)Mother+Fare+Family_Size』下取得的,结果如下(抱歉,博主君commit的时候手抖把页面关了,于是没截着图,下面这张图是在我得到最高分之后,用这次的结果重新make commission的,截了个图,得分是0.79426,不是目前我的最高分哈,因此排名木有变…):

    做完feature engineering调整之后的结果

    9.3 learning curves

    有一个很可能发生的问题是,我们不断地做feature engineering,产生的特征越来越多,用这些特征去训练模型,会对我们的训练集拟合得越来越好,同时也可能在逐步丧失泛化能力,从而在待预测的数据上,表现不佳,也就是发生过拟合问题。

    从另一个角度上说,如果模型在待预测的数据上表现不佳,除掉上面说的过拟合问题,也有可能是欠拟合问题,也就是说在训练集上,其实拟合的也不是那么好。

    额,这个欠拟合和过拟合怎么解释呢。这么说吧:

    • 过拟合就像是你班那个学数学比较刻板的同学,老师讲过的题目,一字不漏全记下来了,于是老师再出一样的题目,分分钟精确出结果。but数学考试,因为总是碰到新题目,所以成绩不咋地。
    • 欠拟合就像是,咳咳,和博主level差不多的差生。连老师讲的练习题也记不住,于是连老师出一样题目复习的周测都做不好,考试更是可想而知了。

    而在机器学习的问题上,对于过拟合欠拟合两种情形。我们优化的方式是不同的。

    对过拟合而言,通常以下策略对结果优化是有用的:

    • 做一下feature selection,挑出较好的feature的subset来做training
    • 提供更多的数据,从而弥补原始数据的bias问题,学习到的model也会更准确

    而对于欠拟合而言,我们通常需要更多的feature,更复杂的模型来提高准确度。

    著名的learning curve可以帮我们判定我们的模型现在所处的状态。我们以样本数为横坐标,训练和交叉验证集上的错误率作为纵坐标,两种状态分别如下两张图所示:过拟合(overfitting/high variace),欠拟合(underfitting/high bias)

    过拟合

    欠拟合

    我们也可以把错误率替换成准确率(得分),得到另一种形式的learning curve(sklearn 里面是这么做的)。

    回到我们的问题,我们用scikit-learn里面的learning_curve来帮我们分辨我们模型的状态。举个例子,这里我们一起画一下我们最先得到的baseline model的learning curve。

    import numpy as np
    import matplotlib.pyplot as plt
    from sklearn.learning_curve import learning_curve
    
    # 用sklearn的learning_curve得到training_score和cv_score,使用matplotlib画出learning curve
    def plot_learning_curve(estimator, title, X, y, ylim=None, cv=None, n_jobs=1, 
                            train_sizes=np.linspace(.05, 1., 20), verbose=0, plot=True):
        """
        画出data在某模型上的learning curve.
        参数解释
        ----------
        estimator : 你用的分类器。
        title : 表格的标题。
        X : 输入的feature,numpy类型
        y : 输入的target vector
        ylim : tuple格式的(ymin, ymax), 设定图像中纵坐标的最低点和最高点
        cv : 做cross-validation的时候,数据分成的份数,其中一份作为cv集,其余n-1份作为training(默认为3份)
        n_jobs : 并行的的任务数(默认1)
        """
        train_sizes, train_scores, test_scores = learning_curve(
            estimator, X, y, cv=cv, n_jobs=n_jobs, train_sizes=train_sizes, verbose=verbose)
        
        train_scores_mean = np.mean(train_scores, axis=1)
        train_scores_std = np.std(train_scores, axis=1)
        test_scores_mean = np.mean(test_scores, axis=1)
        test_scores_std = np.std(test_scores, axis=1)
        
        if plot:
            plt.figure()
            plt.title(title)
            if ylim is not None:
                plt.ylim(*ylim)
            plt.xlabel(u"训练样本数")
            plt.ylabel(u"得分")
            plt.gca().invert_yaxis()
            plt.grid()
        
            plt.fill_between(train_sizes, train_scores_mean - train_scores_std, train_scores_mean + train_scores_std, 
                             alpha=0.1, color="b")
            plt.fill_between(train_sizes, test_scores_mean - test_scores_std, test_scores_mean + test_scores_std, 
                             alpha=0.1, color="r")
            plt.plot(train_sizes, train_scores_mean, 'o-', color="b", label=u"训练集上得分")
            plt.plot(train_sizes, test_scores_mean, 'o-', color="r", label=u"交叉验证集上得分")
        
            plt.legend(loc="best")
            
            plt.draw()
            plt.show()
            plt.gca().invert_yaxis()
        
        midpoint = ((train_scores_mean[-1] + train_scores_std[-1]) + (test_scores_mean[-1] - test_scores_std[-1])) / 2
        diff = (train_scores_mean[-1] + train_scores_std[-1]) - (test_scores_mean[-1] - test_scores_std[-1])
        return midpoint, diff
    
    plot_learning_curve(clf, u"学习曲线", X, y)
    

    学习曲线

    在实际数据上看,我们得到的learning curve没有理论推导的那么光滑哈,但是可以大致看出来,训练集和交叉验证集上的得分曲线走势还是符合预期的。

    目前的曲线看来,我们的model并不处于overfitting的状态(overfitting的表现一般是训练集上得分高,而交叉验证集上要低很多,中间的gap比较大)。因此我们可以再做些feature engineering的工作,添加一些新产出的特征或者组合特征到模型中。

    10.模型融合(model ensemble)

    好了,终于到这一步了,我们要祭出机器学习/数据挖掘上通常最后会用到的大杀器了。恩,模型融合。

    『强迫症患者』打算继续喊喊口号…
    『模型融合(model ensemble)很重要!』
    『模型融合(model ensemble)很重要!』
    『模型融合(model ensemble)很重要!』
    重要的事情说三遍,恩,噢啦。

    先解释解释,一会儿再回到我们的问题上哈。
    啥叫模型融合呢,我们还是举几个例子直观理解一下好了。

    大家都看过知识问答的综艺节目中,求助现场观众时候,让观众投票,最高的答案作为自己的答案的形式吧,每个人都有一个判定结果,最后我们相信答案在大多数人手里。

    再通俗一点举个例子。你和你班某数学大神关系好,每次作业都『模仿』他的,于是绝大多数情况下,他做对了,你也对了。突然某一天大神脑子犯糊涂,手一抖,写错了一个数,于是…恩,你也只能跟着错了。
    我们再来看看另外一个场景,你和你班5个数学大神关系都很好,每次都把他们作业拿过来,对比一下,再『自己做』,那你想想,如果哪天某大神犯糊涂了,写错了,but另外四个写对了啊,那你肯定相信另外4人的是正确答案吧?

    最简单的模型融合大概就是这么个意思,比如分类问题,当我们手头上有一堆在同一份数据集上训练得到的分类器(比如logistic regression,SVM,KNN,random forest,神经网络),那我们让他们都分别去做判定,然后对结果做投票统计,取票数最多的结果为最后结果

    bingo,问题就这么完美的解决了。

    模型融合可以比较好地缓解,训练过程中产生的过拟合问题,从而对于结果的准确度提升有一定的帮助。

    话说回来,回到我们现在的问题。你看,我们现在只讲了logistic regression,如果我们还想用这个融合思想去提高我们的结果,我们该怎么做呢?

    既然这个时候模型没得选,那咱们就在数据上动动手脚咯。大家想想,如果模型出现过拟合现在,一定是在我们的训练上出现拟合过度造成的对吧。

    那我们干脆就不要用全部的训练集,每次取训练集的一个subset,做训练,这样,我们虽然用的是同一个机器学习算法,但是得到的模型却是不一样的;同时,因为我们没有任何一份子数据集是全的,因此即使出现过拟合,也是在子训练集上出现过拟合,而不是全体数据上,这样做一个融合,可能对最后的结果有一定的帮助。对,这就是常用的Bagging。

    我们用scikit-learn里面的Bagging来完成上面的思路,过程非常简单。代码如下:

    from sklearn.ensemble import BaggingRegressor
    
    train_df = df.filter(regex='Survived|Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass.*|Mother|Child|Family|Title')
    train_np = train_df.as_matrix()
    
    # y即Survival结果
    y = train_np[:, 0]
    
    # X即特征属性值
    X = train_np[:, 1:]
    
    # fit到BaggingRegressor之中
    clf = linear_model.LogisticRegression(C=1.0, penalty='l1', tol=1e-6)
    bagging_clf = BaggingRegressor(clf, n_estimators=20, max_samples=0.8, max_features=1.0, bootstrap=True, bootstrap_features=False, n_jobs=-1)
    bagging_clf.fit(X, y)
    
    test = df_test.filter(regex='Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass.*|Mother|Child|Family|Title')
    predictions = bagging_clf.predict(test)
    result = pd.DataFrame({'PassengerId':data_test['PassengerId'].as_matrix(), 'Survived':predictions.astype(np.int32)})
    result.to_csv("/Users/HanXiaoyang/Titanic_data/logistic_regression_bagging_predictions.csv", index=False)
    

    然后你再Make a submission,恩,发现对结果还是有帮助的。

    ensemble之后的结果

    11.总结

    文章稍微有点长,非常感谢各位耐心看到这里。
    总结的部分,我就简短写几段,出现的话,很多在文中有对应的场景,大家有兴趣再回头看看。

    对于任何的机器学习问题,不要一上来就追求尽善尽美,先用自己会的算法撸一个baseline的model出来,再进行后续的分析步骤,一步步提高

    在问题的结果过程中:

    • 『对数据的认识太重要了!』
    • 『数据中的特殊点/离群点的分析和处理太重要了!』
    • 『特征工程(feature engineering)太重要了!』
    • 『模型融合(model ensemble)太重要了!』

    本文中用机器学习解决问题的过程大概如下图所示:
    机器学习解决问题的过程

    12.关于数据和代码

    本文中的数据和代码已经上传至github中,欢迎大家下载和自己尝试。

    展开全文
  • 逻辑性 从事软件开发行业的同学们或多或少都具有相当不错的逻辑性,毕竟编程开发本身就是逻辑性较强的任务。但是大家是否考虑过这种逻辑性应该怎么应用到社交技巧上?下面就跟着笔者一起来详细分析下吧。 逻辑思维...

    逻辑性

    从事软件开发行业的同学们或多或少都具有相当不错的逻辑性,毕竟编程开发本身就是逻辑性较强的任务。但是大家是否考虑过这种逻辑性应该怎么应用到社交技巧上?下面就跟着笔者一起来详细分析下吧。

    逻辑思维

    当逻辑性上升到软技能(社交技巧)层面,就成了逻辑思维。

    逻辑思维一直是职场社交和个人职业发展中最重要的软技能之一。它的本质就是在遇到问题时,给你提供一种梳理问题、分析问题及解决问题的方法论。例如,当你需要向领导陈述问题时,你可以运用逻辑思维梳理即将表达的内容,把你的问题背景、论点及结论划分清晰,从而将你的结论层次清晰的传递过对方。

    因此,你的逻辑思维能力越强,相对应的解决问题的能力及沟通技巧就越强。下面的内容,我们将从问题描述、思考过程和沟通表达三个角度对逻辑思维进行展开,问题描述不清楚甚至错误,那么思考过程就失去了意义;同时没有思考过程就没有合理的结论,没有合理的结论也就无法更好的进行沟通表达,因此这两点是逻辑思维的重中之重。

    问题描述

    描述问题的技巧主要为了解决以下的难点:

    • 不能准确的界定问题。
    • 对于问题不能进行较好的拆分
    • 看待问题不全面

    这里,首先我们引入5W2H的方法,该方法在管理学中会经常被提及,但是实际上,我们可以运用到个人决策方面,用来提升自身的问题分析和描述能力。

    提出疑问对于发现问题和解决问题是极其重要的。创造力高的人,都具有擅于提问题的能力,众所周知,提出一个好的问题,就意味着问题解决了一半。提问题的技巧高,可以发挥人的想象力。相反,有些问题提出来,反而挫伤我们的想象力。

    发明者在设计新产品时,常常提出:为什么(Why);做什么(What);何人做(Who);何时(When);何地(Where);如何(How );多少(How much)。这就构成了 5W2H 法的总框架。如果提问题中常有”假如……”、“如果……”、“是否……”这样的虚构,就是一种设问,设问需要更高的想象力。

    5W2H

    一个简单的例子

    其实缺乏 5W2H 导致了问题的场景在生活中是比比皆是的

    A: 老同学,今天遇到你很高兴啊,下次咱们一起去吃饭,聊一聊
    B: 好啊
    A: 88
    B: (黑人问号)心理状态:什么时候去啊?去哪里?AA?
    B: 得出结论: 看来是随便说说的,并不是真的想去

    因此回答这一小节开头的难点,我们来简单分析下。

    不能准确的界定问题

    如果是这一条的话,以后就用这个方法来思考问题。人的首要思维都是靠大脑在动,其实殊不知,很多事情用手和脑同时动,智商会被拉高的,而且思维会很活跃。所以请记下这个方法,在以后思考问题界定问题的时候,就用笔画下这个 5W2H,然后一一的去对入。时间久后,自然而然的锻炼除了大脑和手共同 “ 思考 ”,从而拉升智商的提高。

    看待问题不全面

    思考过程

    有了良好的问题描述,我们就可以开始更加深入的思考了,其实在描述问题的过程中,本身就进行了初步的思考了,不是吗?(回答不是的同学,请继续看上一节 ;( )

    对问题进行更进一步的明确

    明确问题主要有两个方法:

    1. 设定想要的状态,即设定目标或设定参照物。当问题很明确,那么设定“理所当然”的目标即可,如:公司连续两年赤字,那么设定目标为公司盈利即可;当问题不明确,那么需要设定理想目标,如:公司连续 10 年保持全国第四,那么设定目标可考虑 5 年内成为全国第一。
    2. 把问题具体化到能够思考原因的大小,列举具体事例,从事例中归纳问题。如,年轻员工没有朝气,那么我们可以通过列举具体事例:打招呼有气无力、写资料错误率高、辞职率高等,通过这样的细化,我们就知道具体的问题是什么了。

    深挖原因

    深挖原因的关键在于,不停地询问为什么,逐步深入挖掘。如何有逻辑地深入挖掘原因?这里建议使用MECE 分析法。下面会介绍 MECE,但是大家要注意: 不是所有问题都是严格的按照 MECE 的方法来执行,大家可以在学习后,自己总结一套简化版本,适用于日常情况的,生搬硬套总是会落入下乘的。

    MECE 分析法

    该方法是麦肯锡的黄金法则:四步看透问题的本质,精准解决问题!

    MECE,是 Mutually Exclusive Collectively Exhaustive 缩写,中文意思是”相互独立,完全穷尽”。 也就是对于一个重大的议题,能够做到不重叠、不遗漏的分类,而且能够借此有效把握问题的核心,并解决问题的方法。

    所谓的不遗漏、不重叠指在将某个整体(不论是客观存在的还是概念性的整体)划分为不同的部分时,必须保证划分后的各部分符合以下要求:

    • 完整性(无遗漏),指分解工作的过程中不要漏掉某项,意味着问题的细分是在同一维度上并有明确区分、不可重迭的
    • 独立性(无重复),强调每项工作之间要独立,无交叉重叠,意味着问题的分析要全面、周密

    一个例子

    工厂里搞 5S 管理时候培训师经常使用的一个例子,“怎么整理三个抽屉里的杂乱东西”:

    方法一
    1. 抽屉一的东西分类,整理
    2. 抽屉二的东西分类,整理
    3. 抽屉三的东西分类,整理
    方法二
    1. 定义三个类别,三个抽屉分别规定一个类别
    2. 整理抽屉一到一二三
    3. 整理抽屉二到一二三
    4. 整理抽屉三到一二三
    方法三:
    1. 所有东西都拿出来
    2. 规定三个抽屉内物品的用途
    3. 所有的物品按照类别放进去

    我们来评价一下三种方法:
    方法一,程序最复杂,需要至少九个步骤,且结果最差,整理后东西仍然是混乱的,按照 5S 的定义只涉及到整理没涉及到整顿;
    方法二,程序复杂,九个步骤,结果是清晰的;
    方法三,步骤简单,结果清晰

    MECE 的四个步骤

    确定范围。

    也就是要明确当下讨论的问题到底是什么,以及我们想要达到的目的是什么。这个范围决定了问题的边界。这也让”完全穷尽“成为一种可能。换句话说,MECE 中的”完全穷尽“是指有边界的穷尽。

    寻找符合 MECE 的切入点。

    所谓的切入点是指,你准备按什么来分,或者说大家共同的属性是什么。比如,是按颜色分、按大小分、按时间序列分还是按重要性分?这一步是最难的,但也是最关键的。在找切入点的时候,一定要记得以终为始!这个时候一定要反复思考,你当初要解决的【问题】或当初分析的【目的】是什么。换句话说,你希望分类后解决什么问题,得出什么结论。

    找出大的分类后考虑是否可以用 MECE 继续细分。

    当你觉得这些内容已经确定以后,仔细琢磨它们。是不是每一项内容都是独立的、可以清楚区分的事情?如果是,那么你的内容清单就是”相互独立的”。如果不是,对它们进行分类和归纳。

    确认有没有遗漏或重复。

    分完类之后必须重新检视一遍,看看有没有明显的遗漏或重复。建议画出一个金字塔结构图,用可视化的方式比较容易发现是否有重叠项。

    注意事项

    1. 在确立问题的时候,通过类似鱼刺图的方法,在确立主要问题的基础上,再逐个往下层层分解,直至所有的疑问都找到,通过问题的层层分解,可以分析出关键问题和初步的解决问题的思路;
    2. 结合头脑风暴法找到主要问题,然后在不考虑现有资源的限制基础上,考虑解决该问题的所有可能方法,在这个过程中,要特别注意多种方法的结合有可能是个新的解决方法,然后再往下分析,每种解决方法所需要的各种资源,并通过分析比较,从上述多种方案中找到目前状况下最现实最令人满意的答案。

    一个案例: 如何组织一场培训

    确立核心问题

    列出关键点,并且完全穷尽

    检查每一项是否完全独立,如果不是,对它们进行分类和归纳

    再检查是否每一层是否完全独立,而且穷尽

    MECE 总结

    我们会发现这种呈现的结构变成了金字塔样式,每一层都是下一层内容的总结概括,而第一层是要阐述的核心问题(或观点),这就是麦肯锡推崇的金字塔思维结构。使用金字塔结构图可以比较容易地发现是否有重叠项。

    MECE 原则最大好处就在于,对于影响问题产生的所有因素进行层层分解,通过分解得出关键问题所在,以及解决问题的初步思路。无论绩效问题还是业绩问题,都可以通过 MECE 不断归纳总结,梳理思路寻找达到目标的关键点。

    沟通表达

    在问题被描述、思考清楚后,就该沟通表达了,可以遵循论点 -> 结论 -> 理由 -> 行动的框架。

    论点/背景

    论点,一般指接下来谈话的中心内容。论点阐述时经常包含背景介绍,它们往往不可分割,阐述论点,应尽量从对方了解的信息开始阐述。

    我们可以使用上本提到的 5W2H 法来细化论点,同时将来各事件元素(时间、地点、人物、事件、原因、如何进展、进展如何等)梳理清楚。

    举一个简单例子,你想找领导聊聊加班的事情,你不能说 “Boss,关于加班我想找你聊一聊“ 。这样显然没有将事件元素陈述清晰,Boss 会无法判断你谈话的内容,他只能去猜测你将要表达内容是关于加班的哪个方面。正确的论点阐述应该是 “Boss,最近,年轻员工加班时间增加过多了,导致了大家怨言较多,人心浮动,我们是不是应该做一些调整?”, 这样论点就描述清楚了。

    结论

    1. 不要答非所问
      结论和论点要紧密相关,这样易于被听众快速理解接。如果问题是“是/否”类型,那就以是否作为结论开始;如果问题是怎么做,那就回答该怎么做,尽量避免答案过于弯绕。

      例如,领导问你最近你的工作完成的如何?你回答说“还在进行中”,其实就不太合理,领导既然这么问,也许是抱着以下目的的:

      • 阶段性的结果是什么
      • 是否存在一些困难
      • 也许你平时跟领导汇报太少了,所以他对你的状态是不清楚的
      • 对你的关心

      结果,你仅仅是一句“还在进行中”,基本没有给领导提供任何可用的信息。

    2. 遵循金字塔原则:结论先行
      否则倾听者容易产生疲倦。当希望给倾听者以准备时间或者倾听者自行得出结论的时候,我们才将结论放后面。

    3. 理由
      理由的陈述关键点在于前文提到的MECE 分析法, 筛选“合格”理由作为结论的支撑,避免将一个相似的理由分成多个理由来说。那么如何做到符合 MECE 原则?如何筛选“正确合格”理由?

      • 换位思考
        从对方角度设想,选择能让对方信服的理由,所以面对不同的听众,要有不同的侧重
      • 理由的理由
        对于认同感不强的理由,采用“理由的理由”去支持它。“理由的理由”可以是:数据证明、一般常识/规律、事例的累积、已决断的策略、公司规定/制度等
      • 理由推导结论
        有大量事实,但没有结论的场景,我们可以先陈列大量理由,从理由中推导出结论
      • 理由整理分类
        已经有结论,但理由支撑没有思路的时候,我们同样可以先陈列大量理由,再整理分类理由

    在目前互联网行业,有一个很好的结论阐述法:基于数据的结论。因此大家在给予结论时,首先给出基于理由推导出的结论,其次应该尽量用数据来佐证你的结论,提高说服力。

    最后我们再介绍一种科学的由理由推导结论的方法:
    1. 归纳并举:列举理由,通过理由的共通点推测结论,这种推理方式的缺点是显得主观,所以要格外注意考虑例外的情况
    2. 演绎推理型:三段式,”大前提 + 小前提” -> 结论。如果大前提、小前提是错误的,那么演绎推理的结论也是无意义的,所以要注意确保大、小前提是正确的

    最后

    给大家推荐一本书<<零秒思考>>,推荐一篇文章<<使用 A4 纸笔记法 100 天增强逻辑思维>>

    理论懂得再多,如果不实践,那就是纸上谈兵,希望大家最终都能通过实践形成强大的逻辑思维,为未来的成功之路打下坚实的基础。


    展开全文
  • 要求红、绿、黄三种颜色的灯在时钟信号的作用下按P6.30规定的顺序转换状态. 中的1表示"亮", 0表示"灭". 要求电路能自启动, 并尽可能采用中规模集成电路芯片. 分析: 从P6.30中可以看出, 电路共需要8个...

     题目: 设计一个灯光控制逻辑电路. 要求红、绿、黄三种颜色的灯在时钟信号的作用下按表P6.30规定的顺序转换状态. 表中的1表示"亮", 0表示"灭". 要求电路能自启动, 并尽可能采用中规模集成电路芯片.

    分析:

            从表P6.30中可以看出, 电路共需要8个有效状态, 每个有效状态均对应一种红、黄、绿灯的亮灭次序. 

            说到8个有效状态, 我们不免想到用8进制计数器来解决问题: 由于题目中说到"尽可能采用中规模集成电路芯片", 那么就不要再用触发器来进行设计了, 而是选用74160之类的中规模集成电路芯片来解决问题. 对于8个有效状态, 我们可以用同步置数法将74160接成同步8进制加法计数器(接成同步8进制计数器也可以), 之后绘制出下面的状态表.

            如果用多个逻辑门来实现R、Y、G的表达式, 那么无疑是较耗费时间的: 这里直接用3片74151(8选1数据选择器)或2片74153(双4选1数据选择器)来实现这三个逻辑函数. 当然, 如果不考虑实际成本, 直接用3片74151实现是最简单的, 但考虑到实际设计成本, 还是应该选择使用芯片数量最少的设计方案(2片双4选1数据选择器74153). 读者若对此处的描述有些许疑惑, 应回过头去研究"使用8选1数据选择器实现3或4变量逻辑函数"和"使用4选1数据选择器实现2或3变量逻辑函数"这两部分内容.

    电路逻辑图(74160+3片74151):

    电路逻辑图(74163+2片74153):

    本题使用中规模集成电路芯片与使用触发器和逻辑门设计电路的对比:

    ①使用中规模集成电路芯片(74160/74163)可采用同步置数法直接将其接成我们需要的同步8进制加法计数器. 而使用触发器和逻辑门的话, 要经历绘制状态表-> 绘制状态激励表 ->求状态激励方程 -> 根据状态激励方程接线共4步.

    ②使用中规模集成电路芯片(74151/74153)可直接(特别是74151)根据真值表将三个逻辑函数实现. 而使用逻辑门的话, 需先将三个逻辑函数化简, 再根据每个逻辑函数的逻辑表达式进行接线: 这样做出现错误的几率较大.

    ③使用中规模集成电路芯片(74160/74163/74151/74153)设计的电路, 可在一定程度上避免"竞争与冒险"的发生. 

    展开全文
  • 七种常见的逻辑门真值

    万次阅读 多人点赞 2020-05-04 13:44:51
    (verilog学习笔记一之常见的逻辑门真值) 1、 与门:所有输入为高时,才会有输出高。逻辑函数表示为F=A*B。 输入A 输入B 输出Y 0 0 0 0 1 0 1 0 0 1 1 1 2、 或门:所有输入为低时,才会有输出低...

    (verilog学习笔记一之常见的逻辑门真值表)

    1、 与门:所有输入为高时,才会有输出高。逻辑函数表示为F=A*B。

    输入A输入B输出Y
    000
    010
    100
    111

    2、 或门:所有输入为低时,才会有输出低。逻辑函数表示为F= A + B。

    输入A输入B输出Y
    000
    011
    101
    111

    3、 非门:逆转输入的高低状态。逻辑函数表示为F=A’。

    输入A输出Y
    01
    10

    4、 与非门:所有输入为高时,才会有输出低。逻辑函数表示为F=(A*B)’。

    输入A输入B输出Y
    001
    011
    101
    110

    5、 或非门:所有输入为低时,才会有输出高。逻辑函数表示为F=(A + B)’。

    输入A输入B输出Y
    001
    010
    100
    110

    6、 异或门:输入相同时输出为低,否则为高。逻辑函数表示为F=A’B + AB’。

    输入A输入B输出Y
    000
    011
    101
    110

    7、 同或门:与异或门相反。输入相同时输出为高,否则为低。逻辑函数表示为F= A*B + A’*B’。

    输入A输入B输出Y
    001
    010
    100
    111
    展开全文
  • 第5章 时序逻辑电路习题 时序逻辑电路的分析就是在给定逻辑电路的基础上列出电路的输出方程驱动方程和状态方程得出状态转换或时序图从而分析电路的逻辑功能 其步骤如下 1由所给的逻辑电路写出输出方程驱动方程 2...
  • 数字逻辑与数字系统总结

    千次阅读 多人点赞 2019-09-08 15:26:58
    一:列出真值 二:写出逻辑函数表达式 三:对逻辑函数进行化简和变换 四:根据简化的逻辑函数表达式画出逻辑图 竞争冒险 竞争是指逻辑门的两个输入信号从不同的电平同时向相反电平跳变的现象 由于竞争的存在,在门电路...
  • verilog之状态机详细解释(二)

    千次阅读 2019-07-14 15:46:22
    一、有限状态机设计的一般步骤: 1) 逻辑抽象,得出状态转换图 就是把给出的一个实际逻辑关系表示为时序逻辑函数,可以用状态转换来描述,也可以用状态转换图来描述...• 按照要求列出电路的状态转换或画出状...
  • 个人收集整理资料 仅供交流学习 勿作商业用途 PLD/CPLD/FPGA 硬件...列出原始状态转移图和 状态优化 状态分配 触发器选型 求解方程式 逻辑图 二脉冲与数字电路课程的回顾 使用中小规模器件设计电路 系列 编码器 <7
  • 一、异步时序逻辑电路特点及分类 1.特点:没有统一时钟脉冲信号,电路状态的改变是外部输入信号变化直接作用的结果; 在状态转移过程中,各存储部件的状态变化发生不同步,不同状态维持时间也不一定相同; 在研究...
  • 一、时序逻辑电路概述 1.时序逻辑电路的结构:由组合电路和存储电路组成,通过反馈回路连成整体 有组合电路和逻辑电路组成,具有对过去输入的... 电路的输出由电路当时的输入和状态(对过去输入的记忆的结果)...
  • 逻辑电路

    千次阅读 多人点赞 2018-08-08 19:52:05
    计算机由非常多的简单逻辑单元组成, 即包含着千万以上个MOS晶体管。 本篇主要介绍MOS晶体管的工作原理和基于MOS晶体管实现逻辑门,以及逻辑门组合构建计算机的组成单元。 MOS晶体管 我们知道电路中电子的流动是...
  • 时序逻辑电路功能的表示方法

    千次阅读 2020-12-18 12:04:58
    目录(1) 逻辑方程组(2) 转换表(3) 状态表(4) 状态图(5) 时序图 (1) 逻辑方程组 ① 激励方程组: 用逻辑函数表达式表示触发器的输入。 ② 转换方程组: 将激励方程组分别带入触发器的特性方程得到。 ③ 输出方程组 ...
  • 16 MyBatisPlus 中逻辑删除

    千次阅读 2018-09-28 11:27:36
    逻辑删除原理就是给中添加一个状态字段。设置状态为删除状态,查询时候排删除状态即可。逻辑删除下的数据依然在数据库中,只是状态改变而已。 实现步骤 1. 全局配置中注入逻辑删除 &lt;!-- 注入逻辑删除 --...
  • 程序猿必备的数电知识之(逻辑代数基础前半篇)

    千次阅读 多人点赞 2020-03-16 18:04:57
    1.只有两种对立逻辑状态的逻辑关系成为二值逻辑。 2.所谓逻辑,在这里是指事物间的因果关系。当两个二进制数码表示不同的逻辑状态时,它们之间可以按照指定的某种因果关系进行推理运算。我们将这种运算成为逻辑运算...
  • FPGA学习之组合逻辑 与时序逻辑

    千次阅读 2013-07-04 14:34:24
    1.在数字电路中,任何时刻输出信号的稳态值仅决定于该时刻各个输入信号取值的组合,而与先前状态无关的逻辑电路叫组合逻辑电路。组合逻辑电路的输入信号和输出信号常常不止一个,组合逻辑电路框图如图2-24所示,其...
  • 逻辑部件

    千次阅读 2007-07-26 13:58:00
    下载第8章逻辑部件现代数字网络是由成百万逻辑门构成的非常复杂的系统。大型复杂数字系统的设计大多采用层次式( hierarchical approach)设计方法,即将复杂的数字网络分解为实现一定功能的各逻辑部件,各逻辑部件...
  • 数电基础:时序逻辑电路

    万次阅读 多人点赞 2019-08-19 08:52:21
    虽然每个数字电路系统可能包含有组合电路,但是在实际应用中绝大多数的系统还包括存储元件,我们将这样的系统描述为时序电路。... 时序逻辑电路是数字逻辑电路的重要组成部分,时序逻辑电路又称时序电路,主...
  • 数字电路-时序逻辑电路

    千次阅读 2020-03-04 17:25:50
    本节将系统介绍时序逻辑电路的工作方法和分析方法、设计方法。首先,概要地讲述了时序逻辑电路在逻辑功能和电路结构上的特点,并详细介绍了分析时序逻辑电路的具体方法和步骤。然后分别介绍了移位寄存器、计数器、...
  • 组合逻辑电路的分析与设计

    千次阅读 多人点赞 2020-01-17 21:36:47
    1.组合逻辑电路的分析方法 首先给组合逻辑电路的一般分析方法,如图1.1所示。 图1.1 组合逻辑电路的一般分析方法 以下图的组合逻辑电路为例。 ...
  • 数字电子技术逻辑运算

    千次阅读 2020-04-23 01:45:49
    数字电子技术学习笔记第一章 数制与编码第二章 基本逻辑运算与集成逻辑门2.1 基本概念2.1.1逻辑变量与逻辑函数2.1.2真值2.2 三种基本逻辑运算2.2.1与逻辑(与运算、逻辑乘)2.2.2 或逻辑(或运算、逻辑加)2.2.3 ...
  • DG3.1——逻辑备库说明

    千次阅读 2012-08-21 22:10:23
    逻辑Standby的准备工作   1 确认操作的对象和语句是否能被逻辑Standby支持 由于逻辑Standby是通过SQL应用来保持与Primary数据库的同步。SQL应用与REDO应用是有很大的区别,REDO应用实际上是在物理Standby...
  • LUT是如何实现千万种逻辑结构的

    千次阅读 2020-02-14 19:44:38
    FPGA是可编程器件,与传统逻辑电路和门阵列(如PAL,GAL及CPLD器件)相比,具有不同的结构,FPGA利用小型查找(16×1RAM)来实现组合逻辑,每个查找连接到一个D触发器的输入端,触发器再来驱动其他逻辑电路或驱动I/O...
  • Oracle逻辑备份与恢复(Data Pump)

    千次阅读 2014-11-27 17:15:49
    1. 备份的类型按照备份方式的不同,可以把备份分为两类:1.1 逻辑备份:指通过逻辑导出对数据进行备份。将数据库中的用户对象导出到一个二进制文件中,逻辑备份使用导入导出工具:EXPDP/IMPDP或EXP/IMP,由于将...
  • 一个逻辑问题的分析:“天堂与地狱的守卫”

    万次阅读 热门讨论 2013-11-14 13:34:09
    最近和朋友讨论一个逻辑问题,据说也是个以前出现过的面试题了。拿出来和大家分享。 问题如下: 你来到两道门口,一道是天堂之门, 一道是地狱之门 。 门口都有一个守卫,只知道守卫一个只说假话,一个只说真话。 ...
  • 数字电子技术之逻辑函数的化简及表示

    千次阅读 多人点赞 2020-05-24 23:21:40
    数字电路的作用是用来表达一个现实的逻辑命题,实现逻辑功能。但是,从 逻辑功能中简单概括得出的逻辑函数,往往不是最简表达式,根据这样的非最简式来实现电路,系统会过于复杂,成本过高,同时,电路运行的安全性...
  • 算术逻辑单元

    千次阅读 2019-06-10 09:46:43
    arithmetic and logic unit 算术逻辑单元,简称ALU,是计算机的数学大脑,也就是计算机里负责运算的组件,比如把两个数相加。基本其他的组件都用到了ALU,它有两个 单元 一个算术单元,一个逻辑单元 算术单元 算术...
  • 【清华大学】《逻辑学概论》笔记

    千次阅读 多人点赞 2020-09-04 01:32:03
    教学视频来源 概要 讲师介绍 陈为蓬 清华大学人文学院 副教授 课程内容 第1讲 什么是逻辑学? 第2讲 逻辑学的产生与发展 第3讲 命题联结词及其基本推理形式 第4讲 复合命题的推理:...1.1 “逻辑逻辑学 什么是逻辑
  • 文章目录A 组合逻辑电路的分析和设计方法A.a 组合逻辑电路的特点及描述A.b 组合逻辑电路的分析A.c 组合逻辑电路的门级电路设计A.d 组合逻辑电路中的竞争-冒险现象 A 组合逻辑电路的分析和设计方法 A.a 组合逻辑电路...
  • 一、逻辑电路的分类 数字电路根据逻辑功能的不同特点,可以分成两大类, ...时序逻辑电路在逻辑功能上的特点是任意时刻的输出不仅取决于当时的输入信号,而且还取决于电路原来的状态,或者说,还与以前的输入有关。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 235,817
精华内容 94,326
关键字:

列出逻辑状态表