読者です 読者をやめる 読者になる 読者になる

ボンジニアの備忘録

凡人エンジニア、通称ボンジニアによる備忘録です。勉強した内容を書いていきます。ジャンル問わずです!間違っていたら指摘をお願いします!

機械学習に使うPythonのメモ

データの前処理

より良いトレーニングセットの構築

参考文献


# 欠損データの検索
import pandas as pd
from io import StringIO

csv_data = '''A,B,C,D
1.0,2.0,3.0,4.0
5.0,6.0,,8.0
10.0,11.0,12.0,
'''
df = pd.read_csv(StringIO(csv_data))
df.isnull().sum()
A    0
B    0
C    1
D    1
dtype: int64
# 欠損値を含む行を削除
df.dropna(axis=0)
A B C D
0 1.0 2.0 3.0 4.0
# 欠損値を含む列を削除
df.dropna(axis=1)
A B
0 1.0 2.0
1 5.0 6.0
2 10.0 11.0
# すべての列がNaNである行だけ削除
df.dropna(how='all')
A B C D
0 1.0 2.0 3.0 4.0
1 5.0 6.0 NaN 8.0
2 10.0 11.0 12.0 NaN
# 非NaN値が4つ未満の行を削除
df.dropna(thresh=4)
A B C D
0 1.0 2.0 3.0 4.0
# 特定の列にNaNが含まれているNaNが含まれている行だけ削除
df.dropna(subset=['C'])
A B C D
0 1.0 2.0 3.0 4.0
2 10.0 11.0 12.0 NaN

補完法

# 平均値補完
from sklearn.preprocessing import Imputer
# 欠損値補完のインスタンス生成(平均値補完)
imr = Imputer(missing_values='NaN', strategy='mean', axis=0)
# データを適合
imr = imr.fit(df)
# 補完実行
imputed_data = imr.transform(df.values)
pd.DataFrame(imputed_data)
0 1 2 3
0 1.0 2.0 3.0 4.0
1 5.0 6.0 7.5 8.0
2 10.0 11.0 12.0 6.0

カテゴリーデータの処理

import pandas as pd
# sapmle data
df = pd.DataFrame([
        ['green','M',10.1,'class1'],
        ['red','L',13.5,'class2'],
        ['blue','XL',15.3,'class1']
    ])
# 列名の変更
df.columns = ['color','size','price','classlabel']
df
color size price classlabel
0 green M 10.1 class1
1 red L 13.5 class2
2 blue XL 15.3 class1
# 順序特徴量のマッピング
# Tシャツのサイズと整数を対応させる
size_mapping = {'XL':3, 'L':2, 'M':1}
# Tシャツのサイズを整数に変換
df['size'] = df['size'].map(size_mapping)
df
color size price classlabel
0 green 1 10.1 class1
1 red 2 13.5 class2
2 blue 3 15.3 class1
import numpy as np
# クラスラベルと整数を対応させるディクショナリーを作成
class_mapping = {label:idx for idx, label in enumerate( np.unique(df['classlabel']) )}
class_mapping
df['classlabel'] = df['classlabel'].map(class_mapping)
df
color size price classlabel
0 green 1 10.1 0
1 red 2 13.5 1
2 blue 3 15.3 0
inv_class_mapping = {v: k for k, v in class_mapping.items()}
df['classlabel'] = df['classlabel'].map(inv_class_mapping)
df
color size price classlabel
0 green 1 10.1 class1
1 red 2 13.5 class2
2 blue 3 15.3 class1
# クラスラベルを整数に変換
from sklearn.preprocessing import LabelEncoder
class_le = LabelEncoder()
y = class_le.fit_transform(df['classlabel'].values)
y
array([0, 1, 0])
# クラスラベルを元に戻す
class_le.inverse_transform(y)
array(['class1', 'class2', 'class1'], dtype=object)
# 名義特徴量のエンコーディング
from sklearn.preprocessing import OneHotEncoder
X = df[['color','size','price']].values
color_le = LabelEncoder()
X[:,0] = color_le.fit_transform(X[:,0])
# one-hot エンコーダの生成
ohe = OneHotEncoder(categorical_features=[0])
# one-hotエンコーディングの実施
ohe.fit_transform(X).toarray()
array([[  0. ,   1. ,   0. ,   1. ,  10.1],
       [  0. ,   0. ,   1. ,   2. ,  13.5],
       [  1. ,   0. ,   0. ,   3. ,  15.3]])
pd.get_dummies(df[['color','size','price']])
size price color_blue color_green color_red
0 1 10.1 0.0 1.0 0.0
1 2 13.5 0.0 0.0 1.0
2 3 15.3 1.0 0.0 0.0

4.5 有益な特徴量の選択

過学習を減らすために必要な事 * さらに多くのトレーニングデータを集める * 正則化を通じて複雑さにペナルティを科す * パラメータの数が少ない、より単純なモデルを選択する * データの次元の数を減らす

L2正則化がモデルの複雑さを低減する方法の一つ L1正則化 無関係な特徴量の個数が多い高次元のデータセットがある






import pandas as pd
df_wine = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/wine/wine.data',header=None)
df_wine.columns = ['Class label','Alcohol', 'Malic acid', 'Ash', 'Alcalinity of ash','Magnesium',
                   'Total phenols', 'Flavanoids','Nonflavanoid phenols','Proanthocyanins','Color intensity','Hue','OD','Proline']
from sklearn.cross_validation import train_test_split
X, y = df_wine.iloc[:,1:].values, df_wine.iloc[:,0].values
X_train, X_test, y_train,y_test = train_test_split(X, y, test_size=0.3, random_state=0)
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler
stdsc = StandardScaler()
X_train_std = stdsc.fit_transform(X_train)
X_test_std = stdsc.fit_transform(X_test)
lr = LogisticRegression(penalty='l1', C=0.1)
lr.fit(X_train,y_train)
print('Training accuracy:', lr.score(X_train_std,y_train))
lr.coef_
Training accuracy: 0.862903225806





array([[ 0.        ,  0.        ,  0.        , -0.39570919, -0.04408174,
         0.        ,  0.        ,  0.        ,  0.        ,  0.        ,
         0.        ,  0.        ,  0.0144286 ],
       [ 0.05863568, -0.01814456,  0.        ,  0.1203236 ,  0.04329638,
         0.        ,  0.        ,  0.        ,  0.        , -0.92300575,
         0.        ,  0.        , -0.00569571],
       [ 0.        ,  0.        ,  0.        ,  0.        , -0.00170408,
         0.        , -1.14833783,  0.        ,  0.        ,  0.67990963,
         0.        ,  0.        , -0.00322748]])
%matplotlib inline
import matplotlib.pyplot as plt
fig = plt.figure()
ax = plt.subplot(111)
colors = ['blue','green','red','cyan','magenta','yellow','black','pink','lightgreen','lightblue','gray','indigo','orange']
weights, params = [],[]

for c in np.arange(-4, 6):
    lr = LogisticRegression(penalty='l1', C=10**c, random_state=0)
    lr.fit(X_train_std,y_train)
    weights.append(lr.coef_[1])
    params.append(10**c)
    
weights = np.array(weights)
for column, color in zip(range(weights.shape[1]), colors):
    plt.plot(params, weights[:, column], label=df_wine.columns[column + 1], color=color)

plt.axhline(0, color='black', linestyle='--', linewidth=3)
plt.xlim([10**(-5), 10**5])
plt.ylabel('weight coefficient')
plt.xlabel('C')
plt.xscale('log')
plt.legend(loc='upper left')
ax.legend(loc='upper center', bbox_to_anchor=(1.38,1.03), ncol=1,fancybox=True)
plt.show()

png

4.5.2 逐次特徴選択アルゴリズム

次元削減法は主に二つのカテゴリーに分かれます。 * 特徴選択
元の特徴量の一部を選択する。 * 特徴抽出
新しい特徴部分空間を生成するために特徴量の集合から情報を抽出する。

逐次特徴選択アルゴリズムは貪欲探索アルゴリズムの一種
貪欲探索アルゴリズムは元々の「d次元」の特徴空間を「k次元」の特徴部分空間に削減するために使用される(k<d)

目的
* 特徴量の部分集合を自動的に選択することで、計算効率の改善。 * 無関係な特徴量やノイズを取り除くことで、モデルの汎化誤差を削減すること

逐次後退選択(SBS)は逐次特徴選択アルゴリズムで、その目的は元々の特徴空間の次元を減らすことにあり、分類機の性能の低下を最小限に抑えた上で計算効率を改善する。

# SBS
from sklearn.base import clone
from itertools import combinations
import numpy as np
from sklearn.cross_validation import train_test_split
from sklearn.metrics import accuracy_score

class SBS():
    """
    逐次後退選択を実行するクラス
    """

    def __init__(self, estimator, k_features, scoring=accuracy_score,test_size=0.25, random_state=1):
        self.scoring = scoring
        self.estimator = clone(estimator)
        self.k_features = k_features
        self.test_size = test_size
        self.random_state = random_state
        
    def fit(self, X, y):
        # トレーニングデータとテストデータに分割
        X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=self.test_size, random_state=self.random_state)
        # すべての特徴量の個数、列インデックス
        dim = X_train.shape[1]
        self.indices_ = tuple(range(dim))
        self.subsets_ = [self.indices_]
        # すべてのスコアの特徴量を用いてスコアを算出
        score = self._calc_score(X_train, y_train, X_test, y_test, self.indices_)
        
        # スコアを格納
        self.scores_ = [score]
        # 指定した特徴量の個数になるまで処理を反復
        while dim > self.k_features:
            # 空のリストの生成(スコア、列インデックス)
            scores = []
            subsets = []
            
            # 特徴量の部分集合を表す列インデックスの組み合わせごとに処理を反復
            for p in combinations(self.indices_ , r=dim-1):
                # スコアを算出して格納
                score = self._calc_score(X_train, y_train, X_test, y_test, p)
                scores.append(score)
                # 特徴量の部分集合を表す列インデックスのリストを格納
                subsets.append(p)
                
            # 最良のスコアのインデックスを抽出
            best = np.argmax(scores)
            # 最良のスコアとなる列インデックスを抽出して格納
            self.indices_ = subsets[best]
            self.subsets_.append(self.indices_)
            # 特徴量の個数を1つだけ減らして次のステップへ
            dim -= 1
            
            # スコアを格納
            self.scores_.append(scores[best])
            
        self.k_score_ = self.scores_[-1]
        
        return self
    
    def transform(self, X):
        # 抽出した特徴量を返す
        return X[:, self.indices_]
    
    def _calc_score(self, X_train, y_train, X_test, y_test, indices):
        # 指定された列番号indicesの特徴量を抽出してモデルに適合
        self.estimator.fit(X_train[:, indices], y_train)
        # テストデータを用いてクラスラベルを予測
        y_pred = self.estimator.predict(X_test[:, indices])
        # 真のクラスラベルと予測値を用いてスコアを算出
        score = self.scoring(y_test, y_pred)
        return score
from sklearn.neighbors import KNeighborsClassifier
import matplotlib.pyplot as plt
%matplotlib inline
# k近傍分類器のインスタンスを生成
knn = KNeighborsClassifier(n_neighbors=2)
# 逐次後退選択のインスタンスを生成
sbs = SBS(knn, k_features=1)
# 逐次後退選択を実行
sbs.fit(X_train_std,y_train)
# 近傍点の個数リスト
k_feat = [len(k) for k in sbs.subsets_]
plt.plot(k_feat, sbs.scores_, marker='o')
plt.ylim([0.7, 1.1])
plt.ylabel('Accuracy')
plt.xlabel('Number of feature')
plt.grid()
plt.show()

k5 = list(sbs.subsets_[8])
print(df_wine.columns[1:][k5])

Elastic BeanstalkにMeCabを入れてみた。

Elastic Beanstalkに形態素解析ができるライブラリーのMeCabを入れてみました。

Elastic Beanstalk(Worker)を作成している前提です。

環境:python3.4.3

1. MeCabのインストール

僕の場合はmecabフォルダを作ってそこでインストール作業しました。

$ pwd
/home/ec2-user/mecab

インストールに必要なプログラムをインストールしておきます

$ sudo yum install -y gcc-c++ gcc-objc++ gcc41-c++ make

必要なファイルをwgetで取得する(もしくはcurl -O)

$ wget https://mecab.googlecode.com/files/mecab-0.996.tar.gz
$ wget https://mecab.googlecode.com/files/mecab-ipadic-2.7.0-20070801.tar.gz

解凍します。

$ tar xvfz mecab-0.996.tar.gz 
$ tar xvfz mecab-ipadic-2.7.0-20070801.tar.gz 

まずはMeCab本体をインストールします。

$ cd mecab-0.996
$ sudo ./configure --enable-utf8-only --with-charset=utf8
$ sudo make
$ sudo make install

次に辞書のセットアップ

$ cd mecab-ipadic-2.7.0-20070801
$ PATH=$PATH:/usr/local/bin;export PATH
$ sudo ./configure --with-mecab-config=/usr/local/bin/mecab-config --with-charset=utf8
$ sudo make
$ sudo make install

最後に「/usr/local/lib」をld.so.confに追加します。

$ vi /etc/ld.so.conf
include ld.so.conf.d/*.conf
/usr/local/lib

2. Pythonバインディングのインストール

Elastic Beanstalkでプログラムをzip化してデプロイする際にrequirements.txtに
mecab-python3==0.7」を含めてアップすれば大丈夫です。


ただせっかくのElastic Beanstalkなので直接sshで入って、インストール作業するの嫌だなぁと思っていました。何かいい方法があれば教えていただけると助かります。

Python + FlaskによるLINE BOTの開発(AWS使用)

以前、herokuでのLINE BOT作成を記事に書きましたが、もうちょっと実運用を考えた時にどうすればいいか自分なりにググって調べてAWSを選択しました。
yujikawa11.hatenablog.com


なので今回はAWSを使ったLINE BOTの開発を行いました。
なるべくサーバーレスでやりたかったので、いろいろ見ていましたが下記のような流れにしたいなぁと思いました。が!Nat gatewayは高いのでNat用インスタンスを使いました。
【理想】
LINE-->API Gateway-->AWS Lambda-->SNS-->SQS-->Elastic Beanstalk Worker-->Nat gateway-->LINE

【今回】
LINE-->API Gateway-->AWS Lambda-->SNS-->SQS-->Elastic Beanstalk Worker-->Nat用インスタンス-->LINE

f:id:yujikawa11:20160626224606p:plain

1. NATインスタンスの設定

EC2のインスタンス作成より下記のように、すでに作られているNAT用インスタンスを検索して使用します。
f:id:yujikawa11:20160627220125p:plain

特に設定はしてません、そのまま作成完了にしました。

あとはElastic IPを取得し、関連づけを行ってください。
ここで設定したElastic IPはLINE側にも登録します。

2. ネットワーク関連の設定

今回は全て同一のアベイラビリティゾーンで作成しています。

VPCの作成

VPCの作成ボタンより作成を行います。
f:id:yujikawa11:20160627212717p:plain
名前:LineBotVPC
VPC CIDR:10.0.0.0/16

■インターネットゲートウェイの作成

f:id:yujikawa11:20160627213039p:plain
先ほど作成したVPC(LineBotVPC)にアタッチします。

■サブネットの作成

ここではPrivateとPublicの二つを作成します。
f:id:yujikawa11:20160627213138p:plain

名前:Line Bot Private subnet
VPC:LineBotVPC
CIDR:10.0.1.0/24
ルート:NATインスタンスを指定する(送信先:0.0.0.0/0)※作ってなければ後で設定する

名前:Line Bot Public subnet
VPC:LineBotVPC
CIDR:10.0.1.0/24
ルート:インターネットゲートウェイで設定したIDを設定(送信先:0.0.0.0/0)

3. SNSの設定

新規でトピックを作成します。
f:id:yujikawa11:20160627220730p:plain

適当な名前をつけて作成しました。
名前:linebotSNS

4. SQSの設定

新しくキューを作成します。
f:id:yujikawa11:20160627221103p:plain
名前:lineMsgQue
ここでlineMsgQueとlinebotSNSをつなげる設定を行います。
f:id:yujikawa11:20160627221252p:plain

上記のように操作から「SNSトピックへのキューのサブスクライブ」でlinebotSNSを選択します。

5. Lambdaの設定

Lambdaを新規作成します。
今回はPythonを使いました。新しくfunctionを追加して以下のようなプログラムを作成します。
受信した内容をそのままSNSに渡すというものです。

from __future__ import print_function

import json
import boto3
import sys

sns = boto3.client('sns')

print('Loading function')

def handler(event, context):
    jsonstr = json.dumps(event, ensure_ascii = False)
    data = json.dumps({'default' : 'default message' ,'sqs': jsonstr}, ensure_ascii = False)
    #print(data)
    topic = 'LinebotSNSのARN'
    try:
        response = sns.publish(
            TopicArn=topic,
            Message=data,
            MessageStructure='json'
        )
        print("Success Send data>>")
        print(data)
        print("<<")
        return 'Successfully processed'
    except:
        print("ERROR START>>")
        print(data)
        print(sys.exc_info())
        print("<<ERROR END")
        return 'ERROR processed'
        
    return 'END'

■その他設定ついて

f:id:yujikawa11:20160627222120p:plain

RoleはSNSにアクセスできる権限をつけます。
また詳細設定からVPCは作成したVPCを選択し、サブネットはPrivateの方を選択しました。

またAPI Endpointの設定を行います。ここでは「POST」で設定を行い、そこで作成されたURLをLINE側のコールバックURLとして登録してください。
f:id:yujikawa11:20160630095015p:plain

6. Elastic beanstalk Workerの設定

「Create New Application」から新規でアプリケーションを作成します。
名前はLineBotWorkerにしました。
f:id:yujikawa11:20160630084347p:plain

次に「Create New Environment」から新しく環境を作成します。
f:id:yujikawa11:20160630084516p:plain
作成時にWebかWorkerを選択すると思いますが、今回はSQSとの連携があるためWorkerを選択します。
f:id:yujikawa11:20160630084619p:plain

LINEにメッセージを送るプログラム一式をzip化して、zipファイルを選択します。「Configure more options」を選択して詳細設定をしていきます。
f:id:yujikawa11:20160630084711p:plain

詳細設定ではVPCの部分の設定を行います。
Elastic beanstalkのネットワークを作成したVPC(LineBotVPC)にします。
またsubnet vpcはprivateにします。

その後作成をすれば完成です。

7 LINEへの送信プログラムについて

zip化したファイルは以下です。
フレームワークはflaskを使用しています。
プログラム自体はおうむ返しのプログラムです。
application.pyは名前を変更してもいいですが、その際はElastic beanstalk側の
アプリケーション名も変える必要があります。(デフォルトがapplication.py)

application.py

from flask import Flask,request,Response
import logging
import requests
import json
import configparser
inifile = configparser.SafeConfigParser()
inifile.read("./config.ini")

LINEBOT_API_EVENT ='https://trialbot-api.line.me/v1/events'
LINE_HEADERS = {
    'Content-type': 'application/json; charset=UTF-8',
    'X-Line-ChannelID':inifile.get("settings","X-Line-ChannelID"),
    'X-Line-ChannelSecret':inifile.get("settings","X-Line-ChannelSecret"),
    'X-Line-Trusted-User-With-ACL':inifile.get("settings","X-Line-Trusted-User-With-ACL")
}

def post_event(to, content):
    msg = {
        'to': [to],
        'toChannel': 1383378250,
        'eventType': "138311608800106203",
        'content': content
    }
    r = requests.post(LINEBOT_API_EVENT, 
                      headers = LINE_HEADERS, 
                      data = json.dumps(msg)
                      )
    
def post_text(to, text):
    content = {
        'contentType':1,
        'toType':1,
        'text':text,
    }
    post_event(to, content)


application = Flask(__name__)
@application.route("/sender", methods=['POST'])

def callback():
    status = None
    print("Start SQS Message recieve")
    if request.json is None:
        status = Response("", status=415)
    else:
        try:
            print("massages json recieve start")
            message = dict()
            messages = json.loads(request.json['Message'])
            print("massages json recieve end")
            for message in messages['result']:
                text = message['content']['text']
                response = text
                post_text(message['content']['from'], response)
                status = Response("",status=200)
        except Exception as ex:
            logging.exception('Error processing message: %s' % request.json)
            response = Response(ex.message, status=500)
    return status 

if __name__ == "__main__":
   application.run(host = '0.0.0.0')

config.ini

[settings]
X-Line-ChannelID:XXXXXXXXX
X-Line-ChannelSecret:XXXXXXXX
X-Line-Trusted-User-With-ACL:MID
proxy:http://XXXXXXXXXXX

requirements.txt

Flask==0.11.1
Flask-Cors==2.1.2
jsonschema==2.4.0
requests==2.9.1
simplejson==3.8.2
configparser==3.5.0

pythonで形態素解析をやってみた

すぐに解析ができるMeCabJanomeについてメモです。

【環境】
python3.5.1
Janome 0.2.8
mecab-python3 0.7

Janomeの場合



まずはJanomeのインストールから始めます。
$ pip install janome

次にpythonでソースを書きます。

from janome.tokenizer import Tokenizer

t = Tokenizer()
text = "pythonで形態素解析をやってみた"
tokens = t.tokenize(text)
res = ""
for token in tokens:
    print(token)

【出力結果】
python 名詞,一般,*,*,*,*,*,*,*
で 助詞,格助詞,一般,*,*,*,で,デ,デ
形態素 名詞,一般,*,*,*,*,形態素,ケイタイソ,ケイタイソ
解析 名詞,サ変接続,*,*,*,*,解析,カイセキ,カイセ
を 助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
やっ 動詞,自立,*,*,五段・ラ行,連用タ接続,やる,ヤッ,ヤッ
て 助詞,接続助詞,*,*,*,*,て,テ,テ
み 動詞,非自立,*,*,一段,連用形,みる,ミ,ミ
た 助動詞,*,*,*,特殊・タ,基本形,た,タ,タ

token.surfaceとすると分けられた文字だけ取得できます。

MeCabの場合

まずはMeCabのインストールから始めます。
$ pip install mecab-python3

import MeCab

text="pythonで形態素解析をやってみた"
mt = MeCab.Tagger("-Ochasen")
mt.parse('')
node = mt.parseToNode(text)
while node:
    print (node.surface, node.feature)
    node = node.next

【出力結果】
BOS/EOS,*,*,*,*,*,*,*,*
python 名詞,一般,*,*,*,*,*
で 助詞,格助詞,一般,*,*,*,で,デ,デ
形態素 名詞,一般,*,*,*,*,形態素,ケイタイソ,ケイタイソ
解析 名詞,サ変接続,*,*,*,*,解析,カイセキ,カイセ
を 助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
やっ 動詞,自立,*,*,五段・ラ行,連用タ接続,やる,ヤッ,ヤッ
て 助詞,接続助詞,*,*,*,*,て,テ,テ
み 動詞,非自立,*,*,一段,連用形,みる,ミ,ミ
た 助動詞,*,*,*,特殊・タ,基本形,た,タ,タ
BOS/EOS,*,*,*,*,*,*,*,*



思いの外簡単に形態素解析できますね!これをBotの開発とかに組み込むと楽しいかも!!
最終的には自分で形態素解析アルゴリズムを組んでやってみたいですね!!

Python + FlaskによるLINE BOTの開発(Heroku使用)

備忘録的に書いていきます。
今日は「LINE BOT」を作ります!
なんでPythonかというとPHPで作ったことはあったのです。
今後LINE BOT機械学習的な要素を入れたいなぁと思ったので
Pythonを選びました。今絶賛勉強中の言語のため、基本的な書き方すらわかってない
状態ですが、興味がある人は見ていってください。

今回の構成は以下です。
言語:Python 3.5.1
フレームワーク:Flask
サーバ:Heroku(アドオン:Fixie)

PythonフレームワークFlaskを使いました。
Flaskは軽量なフレームワークらしく、非常に簡単にWebアプリが作れるという特徴があると
耳にしたので、使ってみよう!と思いました。一番有名で世界中に使われているDjangoという
フレームワークもありますが、ちょっと学習コスト高そうだったので今回はやめました。

サーバーにはHerokuを使用しています。IBM Bluemixでしたっけ?これも使いたいなぁと
思いましたが、いつでも移行できるだろ!と思ったのでいつも使ってるHerokuにしました。
ただFixieが無料の分だと月に500リクエストしかないためその後は既読無視になるそうですw
まずBotの「Hello World」的なオウム返しBotを作成しました。
アカウント作って・・なんやかんやって部分はネットで調べたらわかると思うので割愛させていただきます。

プログラムは以下です。
隠したい設定ファイルは別途「config.ini」ファイルを作ってそこから呼び出すようにしています。

config.ini

[settings]
X-Line-ChannelID:XXXXXXXXX
X-Line-ChannelSecret:XXXXXXXX
X-Line-Trusted-User-With-ACL:MID
proxy:http://XXXXXXXXXXX

botapp.py

from flask import Flask,request
import requests
import json
import configparser
inifile = configparser.SafeConfigParser()
inifile.read("./config.ini")

LINEBOT_API_EVENT ='https://trialbot-api.line.me/v1/events'
LINE_HEADERS = {
    'Content-type': 'application/json; charset=UTF-8',
    'X-Line-ChannelID':inifile.get("settings","X-Line-ChannelID"),
    'X-Line-ChannelSecret':inifile.get("settings","X-Line-ChannelSecret"),
    'X-Line-Trusted-User-With-ACL':inifile.get("settings","X-Line-Trusted-User-With-ACL")
}

def post_event(to, content):
    msg = {
        'to': [to],
        'toChannel': 1383378250,
        'eventType': "138311608800106203",
        'content': content
    }
    proxy = {'https':inifile.get("settings","proxy")}
    r = requests.post(LINEBOT_API_EVENT, 
                      headers = LINE_HEADERS, 
                      data = json.dumps(msg), 
                      proxies=proxy)
    
def post_text(to, text):
    content = {
        'contentType':1,
        'toType':1,
        'text':text,
    }
    post_event(to, content)


app = Flask(__name__)
@app.route("/callback", methods=['POST'])

def callback():
    messages = request.json['result']    
    for message in messages:
        text = message['content']['text']
        response = text
        post_text(message['content']['from'], response)
    return ''

if __name__ == "__main__":
   app.run(host = '0.0.0.0', port = 443, threaded = True, debug = True)

機械学習の勉強

機械学習の勉強のメモをしておきます!

最小二乗法というと下記のような式が出てきますよね!

{ \displaystyle
    J(w) = \frac{1}{2m} \sum_{i=1}^m (h_w(x^{(i)}) - y^{(i)})^{2}
}

これはある(x,y)という点と予測している式h_w(x)との差の合計を出している式(誤差関数)です。 例えば、(1,5)という点がある場合にモデル式h_w(1)の値と実測値5の値の 差を取る形になります。それを全ての点で行う形です。

まずは簡単な方程式を考えます。

{ \displaystyle
    h_w(x) = w_0 + w_1 x
}

{ \displaystyle
    w_0 = 0
}の時を考えてみる。

式が下記のようになります。
{ \displaystyle
    h_w(x) = w_1 x
}

ではこの時のh_w(w)J(w)を見ていきましょう! 下記の点が観測されたとします。(m=3)
(x^{(1)},y^{(1)})=(1,1)
(x^{(2)},y^{(2)})=(2,2)
(x^{(3)},y^{(3)})=(3,3)
f:id:yujikawa11:20160605182605p:plain

(1) w_1=1の時

h_w(x)=x
{ \displaystyle
    J(w) = \frac{1}{6} \sum_{i=1}^3 (x^{(i)} - y^{(i)})^{2}
}
f:id:yujikawa11:20160605182608p:plain

こちらは当然すべて一致しているわけですから、J(w)=0

(2)w_1=0.5の時

h_w(x)=0.5xとなります。
{ \displaystyle
    J(w) = \frac{1}{6} \sum_{i=1}^3 (0.5x^{(i)} - y^{(i)})^{2}
}
f:id:yujikawa11:20160605182611p:plain

今度は観測値と予測のモデルに誤差が生じていますね!ちょっとJ(w)を計算してみましょう! { \displaystyle
    J(w) = \frac{1}{6} \sum_{i=1}^3 (0.5x^{(i)} - y^{(i)})^{2}=\frac{1}{6}\{(0.5x^{(1)} - y^{(1)})^{2}+(0.5x^{(2)} - y^{(2)})^{2}+(0.5x^{(3)} - y^{(3)})^{2}\}
}
{ \displaystyle
    J(w) = \frac{1}{6}\{(0.5\times1 - 1)^{2}+(0.5\times2 - 2)^{2}+(0.5\times3 - 3)^{2}\} =0.58
}

上記のような形でいろんなw_1を入れてみるとJ(w)は下記のようなグラフになります。
f:id:yujikawa11:20160605182614p:plain

最小二乗法というのはJ(w)のグラフの最小値を求めることになります。上図からもわかるように w_1 = 1の時が誤差が最小となりますので、予測モデルのパラメータはw_0=0w_1=1です。 予測モデルは下記になります。
h_w(x)=x

なんとなーくやりたいことのイメージがついたような気がします。 でも本当にやりたいのは
{ \displaystyle
    h_w(x) = w_0 + w_1 x
}
と定めたのちにそれぞれの係数を求めることですよね。
どうやって求めるかというと最急降下法という方法があります。 簡単に言うと適当なw_1 = w_nをとって、J(w_n)の傾きを求めます。 傾きが0でない場合は、w_1を傾きで引いて更新して同じことを繰り返します。 更新がなくなったら終了です。(もしくは変化しなくなったら)