【論文紹介】An Introduction to Variable and Feature Selection

はじめに

論文リンク

An Introduction to Variable and Feature Selectionhttp://www.jmlr.org/papers/volume3/guyon03a/guyon03a.pdf

著者/所属機関

  • Isabelle Guyon, Clopinet
  • Andre Elisseeff, Max Planck Institute for Biological Cybernetics

投稿日付

  • Journal of Machine Learning Research 3 (2003) 1157-1182

概要

  • 特徴量選択の3つの目的:1.予測精度の向上、2.予測器の高速化と効率化、3.内部処理解釈性の向上
  • この論文はおもに1.予測精度の向上にフォーカスしていて、以下の内容をカバー
  • 目的関数、特徴量の生成、ランキング、選択、効率的な特徴量探索方法、特徴量有効性評価の方法
  • 対象は regression または classification

【論文紹介】Machine Learning: The High-Interest Credit Card of Technical Debt

はじめに

機械学習を含むシステムを運用するうえでの、開発時の注意事項やアンチパターンについてまとめた論文です。

論文リンク

Machine Learning: The High Interest Credit Card of Technical Debt – Google AI

著者/所属機関

投稿日付

  • 2014

1. 概要

ソフトウェア開発では、技術的負債という概念が提唱されており、リリースを優先するあまり、品質をおろそかにする(≒借金)と、保守コストが複利的に膨らむ(≒利子)。借金は、リファクタリング単体テストカバレッジ、文書の整備などを行うことで清算可能である。機械学習を扱うシステムは、複雑さが増す分、隠れた負債が発生し得るので、注意しなければならない。その例として、以下のようなものを挙げている。

  • 境界の浸食 boundary erosion
  • データ依存 data dependencies
  • スパゲッティシステム System-level Spaghetti
  • 外部環境の変化 changes in the external world

2. 境界の浸食 Complex Models Erode Boundaries

従来のソフトウェア開発では、機能ごとにモジュール化するなど、境界を設けることが、保守の観点で有用だったが、機械学習を用いたソフトウェアでは、それが難しい。そもそも機械学習を導入する動機が、所望の振る舞いがif-then ロジックで記述できないからであり、外部データに依存せざるを得ない。

2.1 絡み合い Entanglement

一つでも特徴量や重み、ハイパーパラメータを変更すると、すべての予測結果が変わる (CACE : Changing Anything Changes Everything)。回避策としては、① モデルを独立化させる、または、アンサンブルにする、②モデルの振る舞いを深く理解する、③予測性能の変化がコストとなるように学習する。絡み合いは、機械学習においては先天的な特性であり、Version 1.0のリリースよりも、その後の改善の方が難しいので、注意が必要。

2.2 隠れたフィードバックループ Hidden Feedback Loop

繰り返し外界の振る舞いを学習するシステムは、フィードバックループの中にいることになる。例えば、ウェブサイトで Click Through Rate を予測するシステムで、過去の週のユーザのクリック数を特徴量としていた場合。モデルを改善すると、結果的にクリック数も上昇して、予測結果が変わる、というフィードバックループが生じる。徐々に変化するようなケースは、効果があったかどうかの分析も難しくなる。こうした隠れたフィードバックループはできる限り避けるべき。

2.3 想定外のユーザ Undeclared Consumers

機械学習モデルの出力に対してアクセス制限を設けずに、他のシステムのインプットとして使われる場合も注意が必要。 機械学習モデルの変更が、他のシステムに影響する。また、他のシステムの出力が機械学習モデルの入力に使われていると、隠れたフィードバックループを生じさせるリスクもある。 他のシステムに対して適切なアクセス制限を設けておくこと。

3. データ依存 Data Dependencies Cost More than Code Dependencies

いわゆるコード間依存の静的解析ツールがないため、気づかないうちに巨大で複雑なデータ依存を作ってしまうことがある。

3.1. 不安定なデータ

別のシステムの出力を機械学習システムの入力データとして使用する場合、別のシステムの変更により入力データが定量的に変わってしまうことがある。 対策としては、入力データのバージョンコピーを取っておくこと。

3.2. 使われていないデータ Underutilized Data Dependencies

入力特徴量を含む使われていないデータを残しておくのは、変化に対して不必要に脆弱になるので、コストがかかる。 使われていない特徴量には、以下のようなものがある。

  • 開発初期に使われていたが別の特徴量の追加で冗長になったもの
  • 個別に検証されずに複数まとめて導入されたもの
  • 精度改善にごくわずかしか寄与していないもの

対策としては、定期的に個々の特徴量の影響を評価し、特徴量を見直すこと。不要な特徴量の除去による長期的なメリットをチーム内に周知すること。

3.3. データ依存性の静的解析 Static Analysis of Data Dependencies

データ依存性は静的解析の実施が難しい(そうしたツールが普及していない)。Googleでは、こちらの論文にて紹介されている手法を採用している。

3.4. 修正のパッチワーク Correction Cascades

既存の問題Aに対するモデルaがあったときに、少し異なる問題A'に対して、モデルaを入力として差分のみを学習したモデルa'を得るという考えに陥ってしまいがち。しかし、このアプローチはモデルa'がaに依存することになり、長期的に見るとコストがかかる。対策は、特徴量を追加するなどして、モデルそのものを区別できるようにしておくこと。

4. スパゲッティシステム System-level Spaghetti

機械学習を含むシステムにおけるシステム設計上のアンチパターンについて記載する。

4.1 糊コード Glue Code

汎用的なパッケージを利用することが多く、そのためにたくさんの糊コードを書かなくてはならない。対策としては、汎用的なパッケージのうち、システムに本当に必要な機能の部分だけを再実装すること。

4.2 パイプラインジャングル Pipeline Jungles

新しいデータソースが後になって得られたりするうちに、パイプラインが増えて、ジャングル化する(糊コードの特殊ケース)。対策は、データ収集から特徴抽出までの全体を見渡して、再設計すること。糊コードやパイプラインジャングルの原因の一つに、研究とエンジニアリングで部門が分断されていることにある。Googleでは研究者とエンジニアリの両者が一緒になって、あるいは連携しながら、プロジェクトを進めるようにしている。

4.3 実験用コードの残骸 Dead Experimental Codepaths

別のアルゴリズムの検討などに用いられた実験用コードをそのまま残しておくと、負債につながる。使われなくなったコードや実験用コードは、システムから切り出して別に保存しておくこと。

4.4 設定 Config debt

機械学習ではたくさんの設定が必要なため、設定も負債となりうる。対応としては、設定にもアサーションを入れておくこと、設定の変更に対して視覚的に確認できるようにしておくこと、設定ファイルもコードと同様に慎重に扱い、レビューすること。

5. 外部環境の変化の扱い

機械学習の魅力の一つに外部環境と直接的に作用することができることがある。しかし、外部環境は不安定なことが多く、負債の原因となりうる。

5.1 動的システムにおける固定閾値 Fixed Thresholds in Dynamic Systems

機械学習システムには何らかの閾値を設けることはよくあるが、この閾値の値はマニュアルで決められることが多い。モデルを更新するたびに、これらの閾値も同時にマニュアルで更新する必要があり、保守の観点では手間となる。対策は交差検定の検証データから学習して閾値を決めるようにすること。

5.2 相関の消失 When Correlations No Longer Correlate

モデル作成時に相関のあった特徴量が、外部環境の変化により相関がなくなるとシステムの予測値は大きく異なるものとなる。また因果のない相関も負債の原因となりうるため注意すべきである。

5.3 モニタリングとテスト Monitoring and Testing

外部環境の変化の下では、単体テスト結合テストだけではシステムが意図通りに動作するかの検証には不十分でシステムのリアルタイム監視も必要となる。以下の2つの指標の監視から始めるとよい。

  1. 予測バイアス Prediction Bias システムが意図通りに動作している場合は、予測ラベルの分布と観測されるラベルの分布は等しくなるはずである。 いろいろな次元でのバイアスを得るのは、問題の特定に有効であるだけでなく、自動発報にも使える。

  2. 実行制限 Action Limits 何らかのアクションを実行するのにシステムが使われるような場合、アクションに制限を設けておく。 制限に引っかかった場合は、自動発報し、マニュアルによる調査を行うようにすると良い。

6. 結論: 清算

機械学習を否定するわけでもなく、技術的負債を何としても避けるべきものというつもりもない。ただし、負債が手に負えなくならないように、認識しておくべきである。重要な点は、技術的負債は研究者とエンジニアの両者が注意すべきものであるということである。技術的負債の清算は、新たな理論の証明のようにわくわくするものではないが、革新を起こすには必要不可欠である。

機械学習の品質管理に関する記事やコミュニティなど

はじめに

機械学習の品質管理に関する記事などを集めました。随時更新予定です。(最終更新日 2019/03/24)

論文、発表など

Machine Learning: The High Interest Credit Card of Technical Debt (Google AI)

ai.google

機械学習で泣かないためのコード設計 2018 (TIS株式会社 久保隆宏)

www.slideshare.net

AI時代における品質保証のチャレンジ (NICT 石川 冬樹)

http://research.nii.ac.jp/~f-ishikawa/work/1807-ESTIC18-AI+Testing.pdf

機械学習エンジニアリング・品質保証 (早稲田大学 鷲崎弘宜)

www.slideshare.net

深層学習の品質保証 (PFN 丸山宏)

www.slideshare.net

機械学習システムにおけるソフトウェア品質保証の課題(SQiPシンポジウム2018 企画セッション)

https://www.juse.jp/sqip/symposium/2018/timetable/files/E2_happyou.pdf

コミュニティ

日本ソフトウェア科学会 機械学習工学研究会

AIプロダクト品質保証コンソーシアム

セブ島語学留学(2週間)の持ち物

この記事は?

セブ島語学留学に行くための持ち物の備忘録です。

スペック

  • 30代半ばのサラリーマン
  • 滞在期間は2週間
  • 1月下旬出国(一応乾季っぽい)
大事なもの
  • パスポート
  • ペソ*1
  • 日本円
  • クレジットカード
  • eチケット
  • まねぱかーど*2
勉強道具
  • シャーペン、替え芯、
  • 消しゴム
  • ペン
  • ノート
  • お気に入りの参考書
エレクトロニクス
  • スマホ
  • タブレット
  • ノートPC
  • 充電器、ACアダプタ*3
  • イヤホン
  • モバイルスピーカー
  • モバイルバッテリー
  • 変換プラグ*4
アメニティ
  • バスタオル
  • フェイスタオル
  • 歯ブラシ、歯磨き粉
  • シャンプー
  • ボディーソープ
  • 洗顔
  • 日焼け止め
  • ティッシュ
  • 爪切り、耳かき
  • 室内スリッパ
衣料品
  • 下着
  • 長ズボン
  • 短パン
  • Tシャツ
  • 上着(はおるもの)
  • キャップ
  • サンダル
  • サングラス
  • 日焼け止め
  • 虫よけ
  • ちっこいかばん
  • 小銭入れ
  • 通学かばん
  • 折り畳み傘
レジャーグッズ(いや、一応。。。)
  • ビーチサンダル
  • 水着
  • 水中メガネ
  • ラッシュガード
食品(日本食が恋しくなると思うので)
  • インスタント食品(袋めん、味噌汁、パスタソース、サトウのごはん)
  • ばんそうこう
  • 酔い止め

*1:空港→ホテルのタクシー代と現地SIMカード購入費用は必須

*2:フィリピンペソはマネパカードのメリット小。こちらを参照。

*3:220V対応品のみ

*4:フィリピンはC型なので不要

これからデータ分析を始めるという人に読んでほしい本

これからデータ分析を始めるという人に、ぜひ読んでほしい一冊のご紹介です。

会社を変える分析の力 (講談社現代新書)

会社を変える分析の力 (講談社現代新書)

著者は大阪ガスの河本薫さん、同社のデータ分析部隊の所長でもあり、この業界では有名人です。

データ分析に関するセミナー(有償)の講師の方にお薦めされた一冊です。

自宅の書庫に眠っていたのを見つけ、お正月に読み返しました。

データ分析を生業とする際の、陥りがちな失敗や、そうならないための心構えから遂行プロセス、成功のポイントまで、事例を交えながら書かれています。

特に、データ分析の結果をビジネスにいかに結びつけるかという論旨は、当たり前ではありますが、本当にその通りだと感じました。

たとえば、分析の「価値」については、以下のように述べられています。

「分析の価値」=「意思決定への寄与度」x「意思決定の重要性」

ここで言う意思決定とは、ビジネスシーンにみられるあらゆるものをさしますが、たとえば、「何かを購入するどうか」や、「何をどれくらい購入するか」などがあるかと思います。

言い換えると、意思決定に寄与しない分析はただの数字遊びになってしまいます。

この他にもたくさん大切なことが書かれていますので、仕事でデータ分析を行う方はご一読されてはいかがかと思います。

最近ではこちらも執筆されたのですね。ぜひこちらも拝読してみたいものです。

最強のデータ分析組織 なぜ大阪ガスは成功したのか

最強のデータ分析組織 なぜ大阪ガスは成功したのか

nvidia のグラボのドライバのアップデート(ubuntu)

UbuntuNVIDIAプロプライエタリードライバを使っているけど、Ubuntu をアップデートした後ログイン画面でループになる。 いろいろ調べた結果、NVIDIA のドライバを入れなおすと解決することがわかったので、やり方をメモしておく。

環境

OS: ubuntu 14.04LTS (64-bit)

Graphic board: Geforce GTX 960

ドライバのダウンロード

nvidia のページから 今回ダウンロードしたファイルは NVIDIA-Linux-x86_64-375.26.run

CUI 画面へ

ログイン画面で ctrl + Alt + F1 キー

古いドライバの削除

sudo apt-get purge nvidia*

GUI サービスの停止

sudo servie lightdm stop

インストーラファイルの実行

先ほどダウンロードしたファイルのあるディレクトリに移動して

chmod 755 ./NVIDIA-Linux-x86_64-375.26.run sudo ./NVIDIA-Linux-x86_64-375.26.run

再起動

sudo reboot

以上

機械学習によるニュース記事の分類

やったこと

テキストデータからの特徴量の抽出

20 newsgroups dataset のうち、トレーニング用の分をロード

20 Newsgroupsは、ニュース記事約20,000本を、20カテゴリーに分類したアノテーション済のありがた〜いデータセット

import numpy as np
from sklearn.datasets import fetch_20newsgroups
twenty_train = fetch_20newsgroups(subset='train', shuffle='true',random_state=1)

データセットの内容の確認

ニュース記事のカテゴリー名はこんな感じ。

twenty_train.target_names
['alt.atheism',
 'comp.graphics',
 'comp.os.ms-windows.misc',
 'comp.sys.ibm.pc.hardware',
 'comp.sys.mac.hardware',
 'comp.windows.x',
 'misc.forsale',
 'rec.autos',
 'rec.motorcycles',
 'rec.sport.baseball',
 'rec.sport.hockey',
 'sci.crypt',
 'sci.electronics',
 'sci.med',
 'sci.space',
 'soc.religion.christian',
 'talk.politics.guns',
 'talk.politics.mideast',
 'talk.politics.misc',
 'talk.religion.misc']

続いてデータ本体の中身の確認。ニュース記事のテキストデータが格納されている

print len(twenty_train.data)
print type(twenty_train.data)
print(type(twenty_train.data[1]))
print "==============================="
print(twenty_train.data[1])
11314
<type 'list'>
<type 'unicode'>
===============================
From: timmbake@mcl.ucsb.edu (Bake Timmons)
Subject: Re: Amusing atheists and agnostics
Lines: 66


James Hogan writes:

timmbake@mcl.ucsb.edu (Bake Timmons) writes:
>>Jim Hogan quips:

>>... (summary of Jim's stuff)

>>Jim, I'm afraid _you've_ missed the point.

>>>Thus, I think you'll have to admit that  atheists have a lot
>>more up their sleeve than you might have suspected.

>>Nah.  I will encourage people to learn about atheism to see how little atheists
>>have up their sleeves.  Whatever I might have suspected is actually quite
>>meager.  If you want I'll send them your address to learn less about your
>>faith.
(以下略)

これは、alt.atheismという0番目のクラスらしい

print(twenty_train.target_names[twenty_train.target[1]])
print(twenty_train.target[1])
alt.atheism
0

Bag of Words

文章に登場するそれぞれの単語の登場回数を特徴量として、文章をベクトルで表現するやり方。 登場する単語の種類=特徴の次元数となるので、高次元のベクトルになる。一般的には10万次元以上。 10万次元で、データ数が仮に1万サンプル、float32だとすると必要なメモリ量は 100,000 x 10,000 x 4 bytes = 4GB と大きい。 ほとんどの特徴量の値は、0になるので、高次元だけど疎(Sparse)な行列になるので、non-zeroな要素だけを記憶すれば、使用するメモリ量を節約できる。

Vectorizer

sklearnのVectorizerを使って、文章をベクトルに変換。使い方のメモ書き。

from sklearn.feature_extraction.text import CountVectorizer
test = ['aa aa aa aa', 'aa bb cc dd ee', 'aa', 'aa bb', 'cc dd ee']
CountVect = CountVectorizer(min_df=1)
X_count = CountVect.fit_transform(test)
print X_count.todense()
print 
print CountVect.get_feature_names()
print 
print CountVect.vocabulary_.get(u'dd')
[[4 0 0 0 0]
 [1 1 1 1 1]
 [1 0 0 0 0]
 [1 1 0 0 0]
 [0 0 1 1 1]]

[u'aa', u'bb', u'cc', u'dd', u'ee']

3

というわけで、ニュース記事のデータセットを読ませてみる。

CountVect = CountVectorizer()
X_train_counts = CountVect.fit_transform(twenty_train.data)
print X_train_counts.shape
print type(X_train_counts)
X_train_counts[1,:10] # ndarrayのようには表示されない
(11314, 130107)
<class 'scipy.sparse.csr.csr_matrix'>

<1x10 sparse matrix of type '<type 'numpy.int64'>'
    with 0 stored elements in Compressed Sparse Row format>

特徴量の変換:回数から頻度へ

登場回数を特徴とするのは、次のような問題点がある。 長い記事だと、短い記事に比べて平均の登場回数は大きくなる。 そこで一つの記事内に登場した単語数の和で割って正規化する。 この特徴量をTerm Frequencies、略してTFと言う。 いろんな文章(記事)に登場する単語は情報量が少ない。言い換えると 一部の記事にのみ、登場するような単語がその記事の特徴をよく表していると言える。 そこで、いろんな記事に頻出する単語は重みが小さくなるよう調整する。 この特徴量をTerm Frequency times Inverse Document Frequency (TF-IDF)と呼ぶ。

まずはTFの例。こちらでは、 fit と Transform の2段階でやっている。

from sklearn.feature_extraction.text import TfidfTransformer

tf_transformer = TfidfTransformer(use_idf=False).fit(X_train_counts)
X_train_tf = tf_transformer.transform(X_train_counts)
X_train_tf.shape
(11314, 130107)

次に TF-IDF の例。今度は fit と transform を一括でやる fit_transform() を使っても同じ結果が得られる

tfidf_transformer = TfidfTransformer()
X_train_tfidf = tfidf_transformer.fit_transform(X_train_counts)
X_train_tfidf.shape
(11314, 130107)

分類器の学習

いよいよデータを使って学習、分類器はナイーブベイズを使用。

from sklearn.naive_bayes import MultinomialNB
clf = MultinomialNB().fit(X_train_tfidf, twenty_train.target)

新しい記事のデータセットを与えて、分類してみる。ここでは fit は必要なく、transform のみでよい。

docs_new = ['God is love', 'OpenGL on the GPU is fast']
X_new_counts = CountVect.transform(docs_new)
X_new_tfidf = tfidf_transformer.transform(X_new_counts)
predicted = clf.predict(X_new_tfidf)

識別結果を出力させる

for doc, category in zip(docs_new, predicted):
    print('%r => %s' % (doc, twenty_train.target_names[category]))
'God is love' => soc.religion.christian
'OpenGL on the GPU is fast' => rec.autos

パイプライン

ベクトル変換、正規化、分類をパイプラインで一つに。fitで学習も一行で。 簡単だけど慣れるまでは中身がわかりにくくなるから、分けて書いた方が良さそう。

from sklearn.pipeline import Pipeline
text_clf = Pipeline([('vect', CountVectorizer()),
                     ('tfidf', TfidfTransformer()),
                     ('clf', MultinomialNB())
                     ])
text_clf = text_clf.fit(twenty_train.data, twenty_train.target)

モデルの評価

20newsgroupsに用意されているテスト用データセットを使って、モデルの精度を評価する。

twenty_test = fetch_20newsgroups(subset='test', shuffle='true', random_state=1)
predicted = text_clf.predict(twenty_test.data)
print np.mean(predicted == twenty_test.target)
from sklearn import metrics
print(metrics.classification_report(twenty_test.target, predicted,target_names=twenty_test.target_names))
0.77389803505
                          precision    recall  f1-score   support

             alt.atheism       0.80      0.52      0.63       319
           comp.graphics       0.81      0.65      0.72       389
 comp.os.ms-windows.misc       0.82      0.65      0.73       394
comp.sys.ibm.pc.hardware       0.67      0.78      0.72       392
   comp.sys.mac.hardware       0.86      0.77      0.81       385
          comp.windows.x       0.89      0.75      0.82       395
            misc.forsale       0.93      0.69      0.80       390
               rec.autos       0.85      0.92      0.88       396
         rec.motorcycles       0.94      0.93      0.93       398
      rec.sport.baseball       0.92      0.90      0.91       397
        rec.sport.hockey       0.89      0.97      0.93       399
               sci.crypt       0.59      0.97      0.74       396
         sci.electronics       0.84      0.60      0.70       393
                 sci.med       0.92      0.74      0.82       396
               sci.space       0.84      0.89      0.87       394
  soc.religion.christian       0.44      0.98      0.61       398
      talk.politics.guns       0.64      0.94      0.76       364
   talk.politics.mideast       0.93      0.91      0.92       376
      talk.politics.misc       0.96      0.42      0.58       310
      talk.religion.misc       0.97      0.14      0.24       251

             avg / total       0.82      0.77      0.77      7532

正答率は77%。分類器をSVMに変えて同じことをやってみる。

from sklearn.linear_model import SGDClassifier
text_clf = Pipeline([('vect', CountVectorizer()),
                     ('tfidf', TfidfTransformer()),
                     # ('clf', MultinomialNB())
                     ('clf', SGDClassifier(loss='hinge', penalty='l2',alpha=1e-3, n_iter=5, random_state=1))
                     ])
text_clf = text_clf.fit(twenty_train.data, twenty_train.target)
predicted = text_clf.predict(twenty_test.data)
print np.mean(predicted == twenty_test.target)
print(metrics.classification_report(twenty_test.target, predicted,target_names=twenty_test.target_names))
0.823552841211
                          precision    recall  f1-score   support

             alt.atheism       0.73      0.71      0.72       319
           comp.graphics       0.81      0.69      0.75       389
 comp.os.ms-windows.misc       0.72      0.78      0.75       394
comp.sys.ibm.pc.hardware       0.74      0.68      0.71       392
   comp.sys.mac.hardware       0.82      0.82      0.82       385
          comp.windows.x       0.84      0.76      0.80       395
            misc.forsale       0.84      0.89      0.87       390
               rec.autos       0.91      0.89      0.90       396
         rec.motorcycles       0.92      0.96      0.94       398
      rec.sport.baseball       0.88      0.91      0.89       397
        rec.sport.hockey       0.89      0.99      0.93       399
               sci.crypt       0.84      0.96      0.90       396
         sci.electronics       0.81      0.63      0.71       393
                 sci.med       0.89      0.85      0.87       396
               sci.space       0.83      0.96      0.89       394
  soc.religion.christian       0.74      0.94      0.83       398
      talk.politics.guns       0.69      0.93      0.79       364
   talk.politics.mideast       0.91      0.93      0.92       376
      talk.politics.misc       0.88      0.54      0.67       310
      talk.religion.misc       0.85      0.39      0.53       251

             avg / total       0.83      0.82      0.82      7532

SVMの方が計算に時間はかかるが、正答率は高い。

グリッドサーチを使ったパラメータ調整

from sklearn.grid_search import GridSearchCV
parameters = {'vect__ngram_range': [(1, 1), (1, 2)],
              'tfidf__use_idf': (True, False),
              'clf__alpha': (1e-2, 1e-3)}
gs_clf = GridSearchCV(text_clf, parameters, n_jobs=-1) # n_jobs=-1で全CPUコア使用
gs_clf = gs_clf.fit(twenty_train.data[:5000], twenty_train.target[:5000])
gs_clf.grid_scores_
[mean: 0.85100, std: 0.01120, params: {'vect__ngram_range': (1, 1), 'tfidf__use_idf': True, 'clf__alpha': 0.01},
 mean: 0.85600, std: 0.00817, params: {'vect__ngram_range': (1, 2), 'tfidf__use_idf': True, 'clf__alpha': 0.01},
 mean: 0.63180, std: 0.02141, params: {'vect__ngram_range': (1, 1), 'tfidf__use_idf': False, 'clf__alpha': 0.01},
 mean: 0.63180, std: 0.02189, params: {'vect__ngram_range': (1, 2), 'tfidf__use_idf': False, 'clf__alpha': 0.01},
 mean: 0.85900, std: 0.01004, params: {'vect__ngram_range': (1, 1), 'tfidf__use_idf': True, 'clf__alpha': 0.001},
 mean: 0.85800, std: 0.00623, params: {'vect__ngram_range': (1, 2), 'tfidf__use_idf': True, 'clf__alpha': 0.001},
 mean: 0.77340, std: 0.01131, params: {'vect__ngram_range': (1, 1), 'tfidf__use_idf': False, 'clf__alpha': 0.001},
 mean: 0.79520, std: 0.01469, params: {'vect__ngram_range': (1, 2), 'tfidf__use_idf': False, 'clf__alpha': 0.001}]
best_parameters, score, _ = max(gs_clf.grid_scores_, key=lambda x: x[1])
for param_name in sorted(parameters.keys()):
    print("%s: %r" % (param_name, best_parameters[param_name]))
clf__alpha: 0.001
tfidf__use_idf: True
vect__ngram_range: (1, 1)