In [10]:
# Font
import os.path
import matplotlib
import matplotlib.pyplot as plt
import pylab
font = {'family': 'IPAexGothic'}
matplotlib.rc('font', **font)
markerSize = 12
fontSize = 22
figureSize = (12, 9)
bboxPos = (0.5, -0.1)

import numpy as np

from sklearn.linear_model import LogisticRegression

from sklearn import preprocessing
from sklearn import utils

In [11]:
# PNGとEPSファイルを書き出す
def writePNGandEPS(name):
    plt.savefig("%s/%s.png" % ('./images/', name))
    plt.savefig("%s/%s.eps" % ('./images/', name))

In [12]:
useCache = False

if (useCache):
    learnX = np.load('./cache/sm_learnX.npy')
    learnY = np.load('./cache/sm_learnY.npy')
    learnLabel = np.load('./cache/sm_learnLabel.npy')
    
    testX = np.load('./cache/sm_testX.npy')
    testY = np.load('./cache/sm_testY.npy')
    testLabel = np.load('./cache/sm_testLabel.npy')
    
    x = np.load('./cache/sm_x.npy')
    y = np.load('./cache/sm_y.npy')
    label = np.load('./cache/sm_l.npy')
else:
    dataCount = 100;
    # 0~24の間でランダムにx座標の値を生成する.(時刻データ)
    x = np.sort(np.random.random(size=dataCount) * 24).reshape(-1, 1)
    yWithoutNoise = np.sin(x*np.pi/12);
    # xに対するsin関数の値に,正規分布に従うノイズを足し合わせ,ベースの数値を作る.
    y = yWithoutNoise + 0.1 * np.random.randn(dataCount).reshape(-1, 1)

    # ラベルを切り分ける閾値
    threshold1 = 0.6
    threshold2 = -0.6

    # ラベルデータを閾値から作る
    label = np.ones([dataCount,1])
    label[np.where(y > threshold1)] = 2;
    label[np.where(y < threshold2)] = 0;
    label = label.astype(np.int)

    # ここまでがデータの生成.データを教師データとテストデータに,ランダムで切り分ける.
    # テストデータは,学習に使ってはならない.
    learnIndex = np.random.permutation(dataCount)[0:dataCount/2]
    testIndex = np.random.permutation(dataCount)[dataCount/2:dataCount]

    # 3次の多項式の基底を考えて,xの値を拡張する
    X = np.c_[x, x * x, x * x * x]

    # データを実際に切り分ける
    learnX = X[learnIndex]
    learnY = y[learnIndex]
    learnLabel = label[learnIndex]

    testX = X[testIndex]
    testY = y[testIndex]
    testLabel = label[testIndex]
    
    np.save('./cache/sm_learnX.npy', learnX)
    np.save('./cache/sm_learnY.npy', learnY)
    np.save('./cache/sm_learnLabel.npy', learnLabel)
    
    np.save('./cache/sm_testX.npy', testX)
    np.save('./cache/sm_testY.npy', testY)
    np.save('./cache/sm_testLabel.npy', testLabel)
    
    np.save('./cache/sm_x.npy', x)
    np.save('./cache/sm_y.npy', y)
    np.save('./cache/sm_l.npy', label)

In [13]:
# 書籍用のテキストの書き出し
t = x[10:25,0]
l = label[10:25,0]

for var in range(0, 10):
    print '|%f|%d|' % (t[var], l[var])
    
for var in range(0, 10):
    if l[var]==0:
        print '|%d:%02d|%s|' % (np.floor(t[var]), (t[var] - np.floor(t[var])) * 60, u'メニュー1')
    if l[var]==1:
        print '|%d:%02d|%s|' % (np.floor(t[var]), (t[var] - np.floor(t[var])) * 60, u'メニュー2')
    if l[var]==2:
        print '|%d:%02d|%s|' % (np.floor(t[var]), (t[var] - np.floor(t[var])) * 60, u'メニュー3')


|1.774841|1|
|1.805520|1|
|1.913170|1|
|2.019469|1|
|2.050838|2|
|2.247812|1|
|2.606973|2|
|3.926679|2|
|4.065695|2|
|4.229736|2|
|1:46|メニュー2|
|1:48|メニュー2|
|1:54|メニュー2|
|2:01|メニュー2|
|2:03|メニュー3|
|2:14|メニュー2|
|2:36|メニュー3|
|3:55|メニュー3|
|4:03|メニュー3|
|4:13|メニュー3|

In [14]:
# 学習
log_model = LogisticRegression()
log_model.fit(learnX, learnLabel)

# 性能評価
print(u'学習性能 = %f' % log_model.score(learnX, learnLabel))
print(u'汎化性能 = %f' % log_model.score(testX, testLabel))

# 自前のコードで推論
f = np.dot(log_model.coef_, testX.transpose()) + np.matlib.repmat(log_model.intercept_, 50, 1).transpose()
numerator = np.exp(f)
denominator = np.sum(numerator, axis=0)
p = numerator / denominator
predict = p.transpose().argmax(axis=1)-1

# scikit-learnで推論
predictByScikit = log_model.predict(testX)


学習性能 = 0.920000
汎化性能 = 0.900000

In [15]:
# 描画
index = np.where(label == 0);
pylab.figure(figsize=figureSize)
plt.plot(x[index[0]], np.zeros(index[0].shape), 'rx', label=u'メニュー1', markersize=markerSize)
index = np.where(label == 1);
plt.plot(x[index[0]], np.ones(index[0].shape), 'go', label=u'メニュー2', markersize=markerSize)
index = np.where(label == 2);
plt.plot(x[index[0]], np.ones(index[0].shape)*2, 'bv', label=u'メニュー3', markersize=markerSize)
plt.legend()
plt.ylabel(u'選んだメニューの項目')
plt.xlabel(u'時刻')
plt.xlim(0,24)
plt.rcParams["font.size"] = fontSize
writePNGandEPS('logistic01')
plt.show()



In [16]:
# 過学習のテスト
def testOverfitting(k):
    # 4次の多項式の基底を考えて,xの値を拡張する
    X = np.c_[x]
    for i in range(0, k):
        X = np.c_[X, np.power(x, i+1)]

    # データを実際に切り分ける
    learnX = X[learnIndex]
    learnY = y[learnIndex]
    learnLabel = label[learnIndex]

    testX = X[testIndex]
    testY = y[testIndex]
    testLabel = label[testIndex]

    # 学習
    log_model = LogisticRegression()
    log_model.fit(learnX, learnLabel)

    # 性能評価
    print(u'%d次の多項式を使った場合' % k)
    print(u' ->学習性能 = %f' % log_model.score(learnX, learnLabel))
    print(u' ->汎化性能 = %f' % log_model.score(testX, testLabel))

testOverfitting(1)
testOverfitting(2)
testOverfitting(3)
testOverfitting(4)
testOverfitting(5)
testOverfitting(6)
testOverfitting(7)
testOverfitting(8)
testOverfitting(9)
testOverfitting(16)


1次の多項式を使った場合
 ->学習性能 = 0.640000
 ->汎化性能 = 0.600000
2次の多項式を使った場合
 ->学習性能 = 0.860000
 ->汎化性能 = 0.820000
3次の多項式を使った場合
 ->学習性能 = 0.920000
 ->汎化性能 = 0.920000
4次の多項式を使った場合
 ->学習性能 = 0.800000
 ->汎化性能 = 0.820000
5次の多項式を使った場合
 ->学習性能 = 0.820000
 ->汎化性能 = 0.840000
6次の多項式を使った場合
 ->学習性能 = 0.740000
 ->汎化性能 = 0.720000
7次の多項式を使った場合
 ->学習性能 = 0.760000
 ->汎化性能 = 0.740000
8次の多項式を使った場合
 ->学習性能 = 0.760000
 ->汎化性能 = 0.740000
9次の多項式を使った場合
 ->学習性能 = 0.500000
 ->汎化性能 = 0.440000
16次の多項式を使った場合
 ->学習性能 = 0.400000
 ->汎化性能 = 0.400000

In [17]:
# ソフトマックス関数のレンダリング
k = 3
X = np.c_[x]
for i in range(0, k-1):
    X = np.c_[X, np.power(x, i+1)]

# データを実際に切り分ける
learnX = X[learnIndex]
learnY = y[learnIndex]
learnLabel = label[learnIndex]

testX = X[testIndex]
testY = y[testIndex]
testLabel = label[testIndex]

# 学習
log_model = LogisticRegression()
log_model.fit(learnX, learnLabel)

# 性能評価
print(u' ->学習性能 = %f' % log_model.score(learnX, learnLabel))
print(u' ->汎化性能 = %f' % log_model.score(testX, testLabel))

# 学習したパラメータ
print(log_model.intercept_)
print(log_model.coef_)

# 自前のコードで推論
f = np.dot(log_model.coef_, X.transpose()) + np.matlib.repmat(log_model.intercept_, 100, 1).transpose()
numerator = np.exp(f)

denominator = np.sum(numerator, axis=0)
p = numerator / denominator
predict = p.transpose().argmax(axis=1)-1

# scikit-learnで推論
predictByScikit = log_model.predict(X)

pylab.figure(figsize=figureSize)
plt.plot(x, p[0,:], 'r:', markersize=markerSize, label=u'$p_1$(メニュー1)')
plt.plot(x, p[1,:], 'g--', markersize=markerSize, label=u'$p_2$(メニュー2)')
plt.plot(x, p[2,:], 'b-', markersize=markerSize, label=u'$p_3$(メニュー3)')
plt.legend()
plt.xlabel(u'時刻')
plt.ylabel(u'ソフトマックス関数の出力')
plt.xlim(0,24)
plt.rcParams["font.size"] = fontSize
writePNGandEPS('softmaxResults')
plt.show()


 ->学習性能 = 0.860000
 ->汎化性能 = 0.820000
[-2.1937626   0.74527551 -1.74997676]
[[ 0.04400745  0.04400745  0.0019095 ]
 [-0.09306211 -0.09306211  0.00665486]
 [ 0.79172705  0.79172705 -0.16346025]]

In [18]:
##### CoreML用にモデルとパラメータを書き出す
from coremltools.converters import sklearn  
coreml_model = sklearn.convert(log_model, ["x1", "x2", "x3"], "label")
coreml_model.author = 'Yuichi Yoshida'
coreml_model.license = 'MIT'
coreml_model.short_description = '時刻からよく選ばれるメニューの項目を推定します.'
coreml_model.input_description['x1'] = u'1次の入力値.推定したい値を入力してください.'
coreml_model.input_description['x2'] = u'2次の入力値.推定したい値の2乗を入力してください.'
coreml_model.input_description['x3'] = u'3次の入力値.推定したい値を3乗を入力してください.'
# coreml_model.input_description['x4'] = u'4次の入力値.推定したい値を4乗を入力してください.'
# coreml_model.input_description['x5'] = u'5次の入力値.推定したい値を5乗を入力してください.'
coreml_model.output_description['label'] = u'推定した結果'
coreml_model.save('./MenuPrediction.mlmodel')