V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
rockivy
V2EX  ›  Python

sklearn 文本分类器做预测时,如何拿到预测结果。。。

  •  
  •   rockivy · 2016-01-27 18:41:25 +08:00 · 16473 次点击
    这是一个创建于 3004 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我知道这个问题比较奇怪。。。

    最近在拿 sklearn 做中文文本分类器,
    网上找到的例子都是拿带标签的数据,二八划分后,八成用于训练模型,两成用于测试,
    然后分析测试结果看精确度。

    现在,我已经使用训练数据做好了模型训练(存在文本分类器的对象了),
    拿一段之前数据集里面没有的文本数据,使用训练好的文本分类器做类别预测,
    问题是如何拿到预测的类别的名称呢。。。

    代码如下:

    # cls 是之前已经训练好的文本分类器对象
    pred = clf.predict(X_new)
    

    怎样从预测结果 pred ( ndarray )获取到分类的类别名称呢?

    我有尝试过如下的方式去获取:

    label_list = list()
    for index in pred:
        label_list.append(training_data.target_names[index])
    total = len(label_list)
    label_counts = Counter(label_list)
    
    print basename(file_path)
    for label, cnt in label_counts.most_common(3):
        print '%s of %.4f' % (label, float(cnt)/total)
    

    主要就是用测试结果比对训练数据的标签: training_data.target_names
    但是这样出来的预测类别名称显然不对(测试了多次,包括拿训练数据也再次做了预测,基本都不对)。

    不知道哪位可以指点一二,感激不尽~~~

    第 1 条附言  ·  2016-02-01 14:55:06 +08:00
    已解决,请见下面的回复。
    27 条回复    2016-02-29 09:06:11 +08:00
    h4x3rotab
        1
    h4x3rotab  
       2016-01-27 20:46:48 +08:00 via iPhone
    给点上下文,不然不能理解你说的是什么问题
    rockivy
        2
    rockivy  
    OP
       2016-01-27 21:00:54 +08:00 via Android
    @h4x3rotab 前面就是加载 training_data ,然后用 training_data 做文本分类器的模型训练,
    代码很简单,网上一搜一大把

    我的问题就是上面说的,
    有了训练好的分类器后,用这个分类器去预测一段新的文本,
    分类器的输出 pred 是一个 ndarray ,怎么能映射到训练数据的类别呢?
    3pointer
        3
    3pointer  
       2016-01-27 23:50:01 +08:00
    用的什么分类? pred 输出的是类别的概率
    ike
        4
    ike  
       2016-01-28 00:20:06 +08:00
    @3pointer predict_proba 才返回概率。
    ike
        5
    ike  
       2016-01-28 00:27:52 +08:00
    至少我没看出这段代码有什么问题。支持你“显然不对”的观点的论据是什么?就是这段代码的结果吗?既然你说 accuracy, 有没有试过直接在 label 上计算 accuracy_score(Y_true, Y_pred)?你的 label 是从 0 开始还是从 1 开始?
    KIDJourney
        6
    KIDJourney  
       2016-01-28 00:30:18 +08:00
    training_data.label[pred]
    3pointer
        7
    3pointer  
       2016-01-28 01:19:54 +08:00 via iPhone
    @ike 你说的对,我记错了
    billgreen1
        8
    billgreen1  
       2016-01-28 07:26:09 +08:00
    我尝试理解一下题主的问题:
    skearn 分类,要求 target 是数值型,但题主的 target 是 label ,可能是字符型。比如:A,B,C
    预测出的结果可能是 1 , 2 , 3....,题主想把 1,2,3 映射到 A,B,C

    可以在准备数据的时候 把 target 先转换一下。预测结果再转换回去。
    rockivy
        10
    rockivy  
    OP
       2016-01-28 08:04:01 +08:00
    感谢楼上各位的回复!

    完整的情况是这样的:
    0. sklearn 官方给出的文本分类例子中,常用的是一个叫 20-news-group 的语料,是英文语料;

    1. 因为想做中文文本分类,我用的语料是自己从网易新闻的各个二级频道爬出来的一些新闻正文内容,
    即:娱乐( 100 篇文章),体育( xx 篇),财经( xx 篇),教育( xx 篇),等等;
    前面的频道名称就是分类类别标签,每个分类下面的文章是支持(或者说训练)这个分类的语料;

    2. 在 sklearn 中,先 load_files 加载这些分类和各自的语料,作为 training_data ( training_data.target_names 就是所有类别标签的 list );
    training_data 可以划分出 x_train(新闻正文)和 y_train(类别标签 list ,与 x_train 一一对应)

    3. 然后使用 TFIDFVectorizer (配合 jieba 中文分词器),计算语料中分词的 TFIDF 值,将 x_train 转换为 X_train

    4. 使用 X_train 和 y_train 去训练分类器;我选择的分类器有:朴素贝叶斯, SGDClassifier 这两种;
    其中朴素贝叶斯分类器下,二八划分的语料,得到的测试结果差得令人发指(准确率 30%),就没有作为预测使用(差的原因还没分析出来,不过这是另外一个问题了)

    5. 使用 SGDClassifier 在二八划分的语料中,测试预测准确率在 70%左右;现在就又找了一篇新闻正文,想用这个分类器做分类预测;

    上述过程在 sklearn 官方文档的文本分类器 demo 代码中基本都能找到,就不贴代码了。

    6. 在 pred = clf.predict(X_new) 中,得到的 pred 是一个 ndarray , 里面的值是 training_data.target_names 中每个标签的 index (不是百分百肯定,也有可能这里有误解),
    我猜想:这是对待预测数据中分词结果,就是每个词语属于哪个标签的预测, 所以才会有原 po 中尝试的解决方案,就是统计每个标签的出现次数,将出现次数最多的作为这次预测的结果;

    7. 问题是,按照第 6 步的思路,即使我拿原始训练语料(比如训练用的财经类下面的新闻),再用训练好的模型去预测,得到的类别仍然不对,大多数结果都不是财经类。。。

    不知道这样描述够清楚吗?

    欢迎继续讨论~~~
    有问必答,需要的话可以将完整代码贴出来。
    rockivy
        11
    rockivy  
    OP
       2016-01-28 08:38:32 +08:00 via Android
    @KIDJourney 感谢,一会到公司试试看
    rockivy
        12
    rockivy  
    OP
       2016-01-28 08:40:31 +08:00 via Android
    @billgreen1 基本是这样的,不过 preprocessing 中对 label 的一些处理方式我都试过了,貌似都不对(也有可能是我的用法不对?)
    rockivy
        13
    rockivy  
    OP
       2016-01-28 08:42:52 +08:00 via Android
    @ike 这种方法下,即使我拿训练数据再反过来去做预测,得到的结果大多数都不对。。。叫上面回复第 7 点,举出的网易财经频道新闻数据的例子
    rockivy
        14
    rockivy  
    OP
       2016-01-28 09:38:49 +08:00
    @KIDJourney 刚才 debug 看了下, training_data 这个对象没有 label 属性。。。
    rockivy
        15
    rockivy  
    OP
       2016-01-28 10:10:08 +08:00
    to all ,

    上面第 6 点描述中,对 pred 的理解可能有偏差。
    我又 debug 查看了二八划分数据时,使用 20%的测试数据去做预测并计算误差的地方,
    可以看到:
    **这里的的 pred 是一个跟测试标签 y_test 长度一样的一维数组**
    也就是说,对于一个文档样本数据, pred 的输出应该是**唯一的一个标签** ;

    那么现在问题就来了,
    为什么我 read 单个文档样本做预测时,输出的 pred 不是只有**一个元素的数组**呢。。。
    KIDJourney
        16
    KIDJourney  
       2016-01-28 10:15:33 +08:00
    @rockivy
    好吧。。。我也忘了那个属性叫啥了
    又查了一下好像是 target_name
    predic 返回的是一个和预测数据个数相同的 index ,对应到 target 就好。
    rockivy
        17
    rockivy  
    OP
       2016-01-28 12:42:14 +08:00
    @KIDJourney target_names 确实就是原始训练语料的标签列表。

    现在的情况是,我 debug 了一早上,现在被模型预测的输出结果搞迷糊了
    pred = clf.predict(X_new)

    这个 pred 对象:
    1. 在使用原始语料分割出来的 20%的测试数据(大约有 1000 条)作为 X_new 输入时,
    针对每个语料输出了一个分类类别 id ,是没问题的;

    2. 在使用**一条**新的文本数据(无分类类别,待模型预测)作为 X_new 时, pred 并不是一个分类类别 id ,而是一个一维数组,这里我就不会解析了。。。

    3. 假设把上面的这一条新的文本数据放到一个正确的类别文件夹下面去(其他类别文件夹全部留空),重新 load_files 这部分数据作为测试数据, pred 的输出又是正确:对于这一个文档输入给出了一个预测结果类别 id

    上面的 2 , 3 点就是我目前最困惑的地方。
    rockivy
        18
    rockivy  
    OP
       2016-01-28 18:54:11 +08:00   ❤️ 1
    哈哈, 困扰我两天的问题“算是”解决了!

    不敢说是圆满解决,因为这个解决方案稍显丑陋:

    之前为了加载数据方便,在加载训练数据集时使用了 load_files 方法,
    然后使用 train_test_split 方法划分训练数据和测试数据;

    但是在做预测时,单独 open 了待测试的文本文件(与训练数据不同的方式!),然后做 TFIDF 和预测,
    此时,得到的 pred 结果是有问题的。

    经过尝试和修改,
    现在的做法是:
    将待测试的文本文件,放入一个 unknown 的分类, 然后使用 load_files 加载(与训练数据相同的方式),并使用 train_test_split 方法(设置 test_size 非常大,例如 0.999999 ),使用划分出来的对象做 TFIDF ,然后扔给分类器去预测。
    这样,就可以使用如下的代码拿到分类啦!
    for index in pred:
    label_list.append(training_data.target_names[index])
    rockivy
        19
    rockivy  
    OP
       2016-01-28 18:56:09 +08:00   ❤️ 1
    上面是为了攒人品, 添加的问题解决方案,
    供有需要的同学参考~~~
    RangerWolf
        20
    RangerWolf  
       2016-01-30 22:44:00 +08:00
    我印象中并需要这么麻烦。。。
    能贴到 github 上吗?

    有段时间没玩了~ 想试试看
    rockivy
        21
    rockivy  
    OP
       2016-01-31 17:39:21 +08:00 via Android
    @RangerWolf 可以的,明天到公司后贴,贴好了通知你
    rockivy
        22
    rockivy  
    OP
       2016-02-01 14:50:42 +08:00   ❤️ 1
    @RangerWolf @KIDJourney 及楼上各位:
    代码我放在了:
    https://github.com/rocky1001/Machine-Learning/tree/master/text_classifier

    netease_news_classifier.py 是完整代码(包括模型训练与加载待预测数据)
    netease_traning_data.zip 是使用的训练数据,大约抓取了网易新闻 11 个频道下 3-5 天的新闻正文数据
    netease_predict_data.zip 是待预测数据目录,里面新建了 unknown 这一级 folder ,解决了原 po 的问题。

    有需要的同学可以参考~~~
    demon014
        23
    demon014  
       2016-02-25 09:07:38 +08:00
    @rockivy 我想知道怎么用你的这个去分类 TXT 文件。。。还有怎么去把 TXT 转成这个格式
    O(∩_∩)O 新人求罩
    rockivy
        24
    rockivy  
    OP
       2016-02-25 11:31:47 +08:00   ❤️ 1
    @demon014 很简单,两个方法:
    1. 使用 load_files 加载待分类 txt 文件,则需要按照上面的所说的,新增一级 unknown 目录;
    2. 不使用 load_files 加载,直接将待分类的 txt read 出来,作为 list 的一个 element ,将这个 list 扔给 predict 去做预测即可。(多个 txt 就是多个 list element )
    3. 如果训练或是待分类数据在一个 txt ,以 [行] 来区分,我改了一下 sklearn.datasets.load_files 方法,可以将每行加载为一个样本,代码见:
    https://github.com/rocky1001/Machine-Learning/blob/master/text_classifier/load_files.py
    demon014
        25
    demon014  
       2016-02-29 08:08:15 +08:00
    @rockivy 谢谢,最后弱弱的问一下,你是用啥爬的数据,我想爬一下其他的。
    rockivy
        26
    rockivy  
    OP
       2016-02-29 09:05:16 +08:00   ❤️ 1
    大规模的抓取推荐 scrapy 。
    不过我这边的规模很小,就自己用 python 写了几个简单的小爬虫,在我的 github 上有,你可以找找看。
    rockivy
        27
    rockivy  
    OP
       2016-02-29 09:06:11 +08:00
    @demon014 上面忘记 @了。。。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1121 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 18:36 · PVG 02:36 · LAX 11:36 · JFK 14:36
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.