新型コロナの治療薬ができるまで感染スピードを抑えて大切な人を守りましょう。詳細はこちら

数学が苦手な文系のためのAI・機械学習・ディープラーニング超入門!

AI・機械学習
この記事は約53分で読めます。

このページは、AIや機械学習・ディープラーニング(深層学習)に興味はあるけど、プログラミングも数値解析もさっぱり!というAI初心者の方(私です)のための機械学習入門です。

必要なものはGoogle IDとSignate IDの2つだけ。どちらも無料で利用できます。
(Google IDは開発環境を、Signate IDは学習データを用意するために使用します)

難しい数式は使わずに、機械学習の一連の流れと基礎知識を学べるようにまとめていますので、機械学習やディープラーニング、AIなどに興味のある方はぜひコードを実行しながら読み進めてみて下さい。新しいアイデアを形にする足がかりになれば幸いです。

  1. 事前準備
    1. SIGNATEからデータをダウンロード
    2. データをGoogleドライブへアップロード
  2. Googleコラボで機械学習をはじめる
    1. Googleコラボでノートブックを作成
      1. ノートブックの簡単な使い方
    2. Googleコラボのセットアップ
      1. Googleドライブにマウント
    3. Googleコラボの日本語化&ライブラリの読み込み
  3. 今回の機械学習の目的
    1. 学習データの内容
      1. データの種類について
  4. 機械学習手順1 データの確認と整形
    1. データを読み込む
    2. データを表示する
      1. データの件数を確認する
      2. データを全件表示する
      3. データ型(タイプ)を確認する
      4. 空データ(NaN)の個数を確認
      5. 空データの補完
        1. 数値型データの補完
        2. 文字型データの補完
      6. 空白データ以外の欠損値の補完
    3. データの変換
      1. 文字型を数値型に変換
      2. 数値型を文字型に変換
  5. 機械学習手順2 データの基礎分析
    1. 基礎統計量を確認する
      1. 数値型データの基礎統計量を確認する
      2. 文字型データの基礎統計量を確認する
    2. ヒストグラム
  6. 機械学習手順3 有効な説明変数の選定
    1. データの変化を可視化する
      1. 折れ線グラフを描く
    2. 量的データ同士の相関関係を確認する
      1. ヒートマップ
    3. 質的データと量的データの相関関係を確認する
      1. 箱ひげ図
  7. 機械学習手順4 学習モデルと評価関数の選定
    1. 学習モデル
    2. 評価関数
  8. 機械学習手順5 過学習の防止
  9. 機械学習手順6 モデルの作成・予測・評価
    1. モデルに合わせたデータの準備
      1. ダミー変数化とは
    2. モデルの作成・学習
    3. モデルから予測する
    4. 予測結果を評価する
  10. 機械学習手順⑥ モデルの予測精度を高める
    1. 誤差の多いデータを見つける
    2. 説明変数(特徴量)を作る
    3. モデルの再作成・予測・再評価
  11. まとめ


Google IDまたはSingate IDをまだ持っていない方は、以下の記事を参考にしてIDを取得して下さい。(既にIDを2つともお持ちの方は、スキップして下へ進んで下さい)

Google IDの作成方法【無料で簡単】
このページではGoogleアカウントの作成方法をご説明します。アカウントは無料、かつ1~2分で簡単に作成できるので、サクッと作成して次へ進みましょう! Googleアカウントでできること Googleアカウントがあると『Goog...
SIGNATEのアカウント作成方法【無料で簡単】
このページではAI・機械学習を学ぶ上で必要な学習データを入手するのにに便利な『SIGNATE』のアカウントの作成方法をご説明します。アカウントは無料かつ2~3分で簡単に作成できます。 SINGATEとは 『SIGNATE』は機械...

事前準備

SIGNATEからデータをダウンロード

まずは機械学習に必須である学習データを準備しましょう。

下のリンクからSIGNATEへログインし、「【練習問題】お弁当の需要予測」のデータページを開いて下記3つのデータをダウンロードして下さい。

  • モデル学習用データ (train.csv)
  • モデル検証用データ (test.csv)
  • 応募用サンプルファイル (sample.csv)

リンク:SIGNATE「【練習問題】お弁当の需要予測」データ

ダウンロードファイル

データはファイル名の左にある「ダウンロードマーク」からそれぞれダウンロードできます。

データをGoogleドライブへアップロード

次に学習データをGoogleコラボから読み込めるようにGoogleドライブにアップロードします。

Googleドライブにファイルをアップロードする方法
このページではGoogleドライブに学習データをアップロードする方法をご説明します。アップロードは簡単で2~3分で完了します。 ※Googleドライブの利用にはGoogle IDが必要です。まだGoogle IDをお持ちでない...

上のページを参考に、Googleドライブの「マイドライブ\Colab Notebooks」フォルダの中に「tml-1」(tecurio machine learning-1 の略です)というフォルダを作成し、その中に先ほどダウンロードした3つのファイルをアップロードして下さい。

これで事前準備は完了です。

Googleコラボで機械学習をはじめる

Googleコラボでノートブックを作成

Googleコラボではノートブックというファイルにプログラムを書くことで機械学習を行っていきます。まずは新しいノートブックを作成しましょう。

下のリンクからGoogleコラボを開いて下さい。

リンク:Googleコラボ

googleコラボ

この画面が表示された場合は「キャンセル」を選択します。

Googleコラボ 新規Notebook作成

メニューの「ファイル」から「Pyhton 3 の新しいノートブック」を選択します。

Googleコラボ 新規Notebook作成

このように新しい真っ白なにノートブックが開きます。左上には「untitled0.ipynb」とノートブックのタイトルが表示されています。

Googleコラボ ノートブックの名前変更

タイトルは直接編集可能なので、今回は「tml-1」などご自分がわかりやすい名前に変更しておいて下さい。

これで新しいノートブックが用意できました。

ノートブックの簡単な使い方

少しだけノートブックの使い方の説明です。

ノートブックの入力エリアには「コードセル」と「テキストセル」の2種類のセルがあります。テキストセルは補助的な役割で、メインのプログラムはコードセルに記入します。

googleコラボ コードセルとテキストセル

背景がグレーのエリアが「コードセル」、白いエリアが「テキストセル」です。

Googleコラボ コード・テキスト入力ボタン

「+コード」ボタンでコードセルを、「+テキスト」ボタンでテキストセルを追加します。

「コードセル」

コードセル」にはコード(プログラム)を記述します。基本的に「コードセル」だけ使えれば機械学習が可能です

Googleコラボ コード

1つの「コードセル」には以下の3つが含まれます。

コード入力エリア:ここにコードを記述します。

実行ボタン:「コード入力エリア」に入力したプログラムを実行します。実行中はボタンの見た目が次のように変化します。

各種操作:コードの移動や削除などが行えます。

テキストセル

テキストセル」には、あとで自分が見返す時や、共同作業する人が見た時にわかりやすくなるように、コードの説明を書いたり、参照リンク、参考画像を貼り付けたりできます。

Googleコラボ テキスト

1つの「テキストセル」には以下の3つが含まれます。

テキスト入力エリア:ここにメモなどを記述します。

テキスト装飾:メモ内容を色づけしたり、リンクや画像を挿入したりできます。

各種操作:テキストの移動や削除などが行えます。

Googleコラボのセットアップ

それではGoogleコラボで機械学習をする環境を整えましょう。セットアップといっても難しくなく、慣れれば1分もかからずにできるくらい簡単です。

Googleドライブにマウント

まず始めに、GoogleコラボからGoogleドライブに接続してファイルを読み込めるようにします。ここからはいよいよ実際にコードを書いて実行していきます。

下のコードをコードセル内に書いて下さい。(コピペでもOKです)

from google.colab import drive
 
drive.mount('/content/drive')
%cd "/content/drive/My Drive/Colab Notebooks/tml-1"

コード解説

from google.colab import drive
drive.mount(‘/content/drive’)
GoogleコラボからGoogleドライブに接続するという意味です。
%cd “/content/drive/My Drive/Colab Notebooks/tml-1”
先ほど作成してデータファイルをアップロードした「tml-1」フォルダに移動するという意味です。

それでは画面を見ながら進めていきましょう。

googleコラボからgoogleドライブマウント コード実行

入力したコードを実行するため「実行」をクリックします。

googleコラボからgoogleドライブマウント リンククリック

Googleドライブに接続するための認証コードを発行するリンクが表示されます。リンクをクリックしましょう。

googleコラボからgoogleドライブマウント アカウント選択

アカウント選択ページが表示されます。現在ログインしているアカウントを選択します。

googleコラボからgoogleドライブマウント 許可

アクセス確認ページが表示されます。「許可」を選択して下さい。

googleコラボからgoogleドライブマウント コードをコピー

認証コードが表示されるので、コピーして下さい。

googleコラボからgoogleドライブマウント 認証コードを貼り付ける

Googleコラボに戻って認証コードを貼り付けます。貼り付けたら「Enter」キーを押します。

googleコラボからgoogleドライブマウント マウント完了

上のように表示されたら、GoogleコラボからGoogleドライブへの接続完了です。

(また、コードの左横には「[1]」と表示されます。これは実行が完了した時に表示され、中の数字は実行した順番です。今は最初に実行したコードなので1と表示されています)

では、実際にGoogleドライブのファイルにアクセスできているかどうか、現在の作業フォルダにあるファイルの一覧を確認してみましょう。

「+コード」を押してコードセルを追加し、以下のコードを実行してみて下さい。

%ls

実行結果は次のようになります。

googleコラボからgoogleドライブにマウント lsでファイル一覧表示

このようにアップロードした3つのファイルが表示されたらOKです。

Googleドライブに置いたデータにアクセスできるようになりました。

Googleコラボの日本語化&ライブラリの読み込み

日本語版グラフ描画ライブラリのインストール

Googleコラボは便利な開発環境ですが、初期状態ではグラフに日本語が使えません。次は日本語を使えるようにしましょう。

「+コード」を押してコードセルを追加し、以下のコードを実行して下さい。
※以降、新たにコードを書く時には新しいコードセルを追加してそこに書くようにして下さい。

pip install japanize-matplotlib

コード解説

pip install japanize-matplotlib
pipという管理ツールを使って、「japanize-matplotlib」というグラフを日本語化するモジュールをインストールする。という意味です。

ライブラリの読み込み&グラフ日本語化設定

ライブラリとは、誰でも使える便利なプログラムの集まりです。用途に合わせた様々なライブラリがあります。ここでは機械学習をするのに便利なライブラリを読み込みます。

下のコードを実行して下さい。

import pandas as pd # データ分析ライブラリ
import numpy as np # 数値計算ライブラリ
from matplotlib import pyplot as plt # グラフ描画ライブラリ
import seaborn as sns # グラフ描画ライブラリ
from sklearn.linear_model import LinearRegression as LR # 線形回帰モデル
from sklearn.metrics import mean_squared_error # 評価関数RMSE
import japanize_matplotlib #グラフ日本語化ライブラリ
sns.set(font="IPAexGothic") # 日本語フォント設定

コード解説

import pandas as pd
データ分析のためライブラリ「pandas(パンダス)」を読み込み、pdと略して使用するという意味です。
import numpy as np
数値計算のためライブラリ「numpy(ナムパイ)」を読み込み、npと略して使用するという意味です。
import matplotlib.pyplot as plt
可視化ライブラリmatplotlib(マットプロットリブ)」に含まれるグラフ描画モジュールpyplot」を読み込み、pltと略して使用するという意味です。
import seaborn as sns
先に読み込んだ「matplotlib」を使いやすくするライブラリ「seaborn(シーボーン)」を読み込み、snsと略して使用するという意味です。
from sklearn.linear_model import LinearRegression as LR
機械学習ライブラリ「scikit-learn」から線形回帰モデル作成モジュール「LinearRegression」を読み込み、LRと略して使用するという意味です。
from sklearn.metrics import mean_squared_error
機械学習ライブラリ「scikit-learn」から予測値の誤差を算出して評価するモジュール「mean_squared_error」を読み込んでいます。
import japanize_matplotlib
先に読み込んだ「matplotlib」で日本語を使えるようにするライブラリ「japanize_matplotlib」を読み込むという意味です。
sns.set(font=“IPAexGothic”)
グラフに使用する文字のフォントを「IPAゴシック」という日本語対応フォントに設定しています。

これでGoogleコラボのセットアップも完了し、いつでも機械学習を始められる準備ができました。

今回の機械学習の目的

さっそく機械学習をはじめたいところですが、その前に今回の機械学習の目的を覚えておいて下さい。

今回の機械学習の目的は、徐々に売上が少なくなり経営が苦しくなっているとあるお弁当屋さんの利益向上に貢献することです。日々の売上データを学習データとして機械学習を行い、毎日のお弁当の販売個数を予測して廃棄処分するお弁当を減らすことで余計な材料費を削減できるように手助けをしましょう。

もちろん今回の学習データは練習用で、本当にお弁当屋さんがあるわけではありませんが、実際の機械学習の流れを理解するためにも、このような目標があることを念頭に置いてこの入門を進めて下さい。

学習データの内容

先ほどダウンロードした「train.csv」には、下のような日毎のお弁当の販売データが約1年分入っています。

データの列名内容
datetidインデックスとして使用する日付(yyyy-m-d)
y販売数(目的変数)
week曜日(月~金)
soldout完売フラグ(0:完売せず、1:完売)
nameメインメニュー
kcalおかずのカロリー(kcal)欠損有り
remarks特記事項
event13時開始お弁当持ち込み可の社内イベント
payday給料日フラグ(1:給料日)
weather天気
precipitation降水量。ない場合は “–“
temperature気温

今回のケースでは、曜日や天気・気温などの情報を利用して、販売数(y)を予測できるようになることがゴールです。

(予測するデータのことを目的変数(今回は販売数y)、予測するために使用するデータを説明変数または特徴量(今回は日付・曜日・天気など)といいます)

データの種類について

データは大きく2つの種類に分けられます。量的データ質的データです。

尺度意味
量的データ
(数値に意味がある)
間隔尺度日付、西暦、温度など。
足し算・引き算ができる。
比例尺度販売数、金額、個数など。
足し算・引き算ができ、比率にも意味がある。
質的データ
(区別に使用)
名義尺度メニュー名、性別、名前、idなど。
(数値のidでも数字には意味がない)
順序尺度順位、満足度など。
順序に意味があり大小比較できるが、足し算引き算はできない。

ここでは簡単に数値に意味があって計算できるのが量的データ物事を区別するために使用するのが質的データと覚えておいて下さい。

今回のデータの場合、以下のように分けることができます。
(データの解釈・利用方法によっては異なる分類になることもあります)

データの列名内容データの種類
datetidインデックスとして使用する日付(yyyy-m-d)量的データ
y販売数(目的変数)量的データ
week曜日(月~金)質的データ
soldout完売フラグ(0:完売せず、1:完売)質的データ
nameメインメニュー質的データ
kcalおかずのカロリー(kcal)欠損有り量的データ
remarks特記事項質的データ
event13時開始お弁当持ち込み可の社内イベント質的データ
payday給料日フラグ(1:給料日)質的データ
weather天気質的データ
precipitation降水量。ない場合は “–“量的データ
temperature気温量的データ

機械学習手順1 データの確認と整形

まずはデータの確認です。機械学習をするにあたって、データが最も重要と言っても過言ではないくらいに、データの善し悪しが学習の結果に大きく影響します。

もしデータに不足があったり、間違いがあった場合には、学習自体をイチからやり直さないといけなくなることも少なくありません。そうならないためにも、データに不備がないかしっかりチェックするようにしましょう。

データを読み込む

まず学習データが入っている「train.csv」を読み込みます。(train.csvのtrainは列車ではなく、トレーニング・学習の意味です)

新しいコードセルを追加し、下のコードを実行します。
※以降、新たにコードを書く時には新しいコードセルを追加してそこに書くようにして下さい

# train.csvを読み込む
train = pd.read_csv("train.csv")

コード解説

train = pd.read_csv(“train.csv”)
pandasライブラリのread_csv関数を使い、変数trainの中にtrain.csvファイル内のデータを読み込むという意味です。(変数とはデータの入れ物のことです)

データを表示する

次は読み込んだ「train.csv」のデータを表示してみましょう。

次のコードを実行して下さい。

# データを表示
train.head(10)

コード解説

train.head(10)
head関数を使うと変数trainに読み込まれているデータの先頭5行を表示できます。
ここではカッコ()の中に10を指定することで先頭の10行を表示しています。
似た関数にtail関数があり、こちらは末尾からデータを表示します。

結果は次のようになります。

info関数

先頭10件のデータが表示されました。

お弁当屋さんの売上データらしく、日付(datetime)、販売数(y)、日替わり弁当名(name)、天気(weather)などの情報があるのがわかると思います。

データの件数を確認する

ではデータが全部で何件あるのか確認してみましょう。下のコードを実行して下さい

# データの行列数を表示
train.shape

コード解説

train.shape
shapeプロパティを使うと、データの要素数を確認できます。今回のような行と列からなる2次元配列の場合、(行数、列数)と表示されます。

実行結果は次のようになります。

shapeプロパティ

結果は(行数, 列数)を表すので、12個(列)の項目を持ったデータが、207個(行)あるとわかります。(下の画像では10行目までしか表示していませんが実際は207行のデータがあります)

shapeプロパティの結果とデータの見方

データを全件表示する

データを全件表示したい時は、下のコードを実行して下さい。

pd.set_option('display.max_rows', train.shape[0])
train.head(train.shape[0])

コード解説

pd.set_option(‘display.max_rows’, train.shape[0])
set_option関数を使うとpandasの設定を変更できます。
ここでは「(‘display.max_rows’, pd_data.shape[0])」として、表示最大行数にtrainのデータの行数を設定しています。
train.head(train.shape[0])
「(train.shape[0])」としてデータの行数(shapeプロパティの0番目の値)をhead関数に渡すことで、すべてのデータを表示しています。

(「pd.set_option(‘display.max_rows’, 最大行数)」 とpandasで 最大表示行数を設定する方法もあるのですが、こちらの方法の方が無駄がないのでこちらを採用しました)

データ型(タイプ)を確認する

Pythonのデータ型には大きく分けて数値型と文字列型の2種類があります。

データ型名数値/文字備考
int64数値型整数
float64数値型小数点付きの数
object文字型

表のようにint64とfloat64は数値型データobjectは文字型データと覚えておいて下さい。


それでは各列のデータ型を確認してみましょう。下のコードを実行して下さい。

# 列ごとのデータ型と、空ではないデータ数を表示
train.info()

コード解説

train.info()
info関数を使うと、各列のデータ型(数値か文字か)や、空ではない値の入った行数を確認できます。

実行結果は次のようになります。

info関数

左から「列名」「空白ではないデータの個数」「データ型」の順に表示されます。例えば「kcal」の列には空白ではないデータが166件あり、数値型(float64型)であるとわかります。データは全部で207件なので「kcal」列には41件の空白データがあることも読み取れます。欠損値の処理

欠損値とは値のない空データや、値がないことを意味するデータのことです。欠損値があると正しく学習できないことがあるため対処が必要です。

info関数

先ほど見たtrain.csvのデータにも「NaN」や「–」などと表示されている箇所がありましたが、これらが欠損値にあたります。

空データ(NaN)の個数を確認

「NaN」は値がない空データであることを意味しています。目で確認できるところでは「kcal(カロリー)」「remarks(特記事項)」「event(イベント)」「payday(給料日)」に欠損値があるようです。

では実際にどのくらい空データがあるのか確認してみましょう。下のコードを実行して下さい。

train.isnull().sum() # 空データ件数を表示

コード解説

train.isnull().sum()
isnull関数を使うとデータが空白データであるかを確認できます。また、sum関数を使うとデータの合計を計算できます。2つを組み合わせることで、各列の空データ数を表示します。

実行結果は次のようになります。

空データ数の確認

「kcal」には41個、「remarks」には186個、「event」には193個、「payday」には197個の空データがあることが確認できました。その他の列には空データは無いようです。

空データの補完

空データがある行への対応には空データを含む行を削除する方法と、空データに代わりの値を入れる補完があります。今回はデータ数が少なく空データが多いため補完を行います。

数値型データの補完

まず「kcal(お弁当のカロリー)」です。こちらは数値型データです。今回はカロリーの平均値を算出し、それを空データに代入しましょう。下のコードを実行して下さい。

avg = train['kcal'].mean() # kcalの平均値を算出
train['kcal'] = train['kcal'].fillna(avg) # kcalの空データを平均値で埋める

コード解説

avg = train[‘kcal’].mean()
mean関数を使うとデータの平均値を取得できます。今回は「train[‘kcal’]」としてカロリーの平均値を取得し、それを変数avgの中に入れています。
train[‘kcal’] = train[‘kcal’].fillna(avg)
fillna関数を使うと指定した列に含まれる空データを別の値に置き換えられます。ここではkcal列の空データを変数avgに入れたkcalの平均値に置き換えています。

次に「payday(給料日)」です。給料日には「1」が設定されますが、給料日以外の日はNaNになっています。ここではNaNを「0」で埋めておきましょう。下のコードを実行して下さい。

train['payday'] = train['payday'].fillna(0) # paydayの空データを0で埋める

コード解説

train[‘payday’] = train[‘payday’].fillna(0)
fillna関数を使ってpayday列の空白データを0に置き換えています。
文字型データの補完

次は文字型データの「remarks(特記事項)」です。まずremarksにどのような値が入っているのか確認しましょう。下のコードを実行して下さい。

train['remarks'].value_counts()

コード解説

train[‘remarks’].value_counts()
value_counts関数を使うとデータに含まれる値の一覧を確認できます。今回は「train[‘remarks’]」として特記事項の値の一覧を表示しています。

結果は次のようになります。

value_contents関数

メニューに関する追加メモのような内容ですね。ここでは空データを「なし」で埋めておきましょう。下のコードを実行して下さい。

train['remarks'] = train['remarks'].fillna('なし') # remarksの空データを「なし」で埋める

コード解説

train[‘remarks’] = train[‘remarks’].fillna(‘なし’)
fillna関数を使ってremarks列の空データを「なし」という文字に置き換えています。文字を扱う時は「’」または「”」で文字の前後を挟みます。

同じように「event(社内イベント)」の空データも「なし」で埋めておきましょう。下のコードを実行して下さい。

train['event'] = train['event'].fillna('なし') # eventの空データを「なし」で埋める

コード解説

train[‘event’] = train[‘event’].fillna(‘なし’)
fillna関数を使ってevent列の空データを「なし」という文字に置き換えています。

以上で空データ(NaN)の補完処理が終わりました。それでは空データ件数を確認してみましょう。下のコードを実行して下さい。

train.isnull().sum() # 空データ件数を表示

実行結果は次のようになります。

isnull.sum関数 欠損値の補完後

空白データがなくなりました。

空白データ以外の欠損値の補完

空データ(NaN)はなくなりましたが、「precipitation(降水量)」の列に「降水量なし」を意味する「–」というデータがありました。降水量は数値に意味があり計算ができる量的データですが、「–」は文字型データのため計算に使えません。

計算に使えるよう「–」を「0」に置き換えましょう。

train['precipitation'] = train['precipitation'].replace('--', 0) # 「--」を0に置換

コード解説

train[‘precipitation’] = train[‘precipitation’].replace(‘–‘, 0)
replace関数を使うとある値を別の値に置換できます。今回は「replace(‘–‘, 0)」として「–」を数値の「0」に置換しています。

実行後、Head関数で中身を確認すると下のようになります。

head関数 欠損値補完後

「precipitation(降水量)」の「–」が「0」に変わりました。先程補完した「kcal」「remarks」「event」「payday」も「NaN」がそれぞれ補完した値に変わっています。

これで欠損値の補完が完了しました。

データの変換

次はデータ型を使いやすい型に変換します。

データには量的データと質的データがありました。量的データは計算できる数値データなのでデータ型も数値型であると扱いやすく、質的データは区別するためのデータで計算には使用しないため文字型の方が扱いやすいことが多いです。

ここでは量的データなのに文字型となっているデータを数値型に、質的データなのに数値型となっているデータを文字型に変換していきます。

info関数でデータ型を確認しましょう。

info関数 欠損値補完後

量的データである「datetime(日付)」と「precipitation(降水量)」が文字型(object)に、質的データである「soldout(完売日フラグ)」「payday(給料日フラグ)」が数値型(int64、float64)になっています。

文字型を数値型に変換

まず単純に変換できる「precipitation(降水量)」から始めましょう。下のコードを実行して下さい。

train['precipitation'] = train['precipitation'].astype(np.float) # precipitation列を数値型に変換

コード解説

train[‘precipitation’] = train[‘precipitation’].astype(np.float)
astype関数を使うとデータの型を変更できます。ここでは「astype(np.float)」として「precipitation」列のデータ型を数値型に変換しています。

実行後、info関数でデータ型を確認すると次のようになります。

info関数 降水量を数値型に変換後

「precipitation(降水量)」のデータ型が文字型(object)から数値型(float64)に変わっています。


次に「datetime(日付)」を数値型に変換しましょう。日付データは年・月・日から構成されるため、それぞれ別々に数値型として抜き出します。下のコードを実行して下さい。

train['datetime'] = pd.to_datetime(train['datetime']) # 日付をdatatime型に変換 

train['year'] = train['datetime'].dt.year # 日付型データからyear(年)を取得
train['month'] = train['datetime'].dt.month # 日付型データからmonth(月)を取得 
train['day'] = train['datetime'].dt.day # 日付型データからday(日)を取得 

train = train.drop('datetime', axis=1) # 元の日付データを+削除 

コード解説

train[‘datetime’] = pd.to_datetime(train[‘datetime’])
to_datetime関数を使うとデータをdatetime型に変換できます。
ここでいうdatetime型とはPythonで日付を扱うためのデータ型で、日付データの列名である「datetime」とは関係ありません。
train[‘year’] = train[‘datetime’].dt.year
dt.yearプロパティを使うと日付から年を取得できます。
ここでは変数trainの中に新たに「year」という列を追加して、そこに年を入れています。
train[‘month’] = train[‘datetime’].dt.month
dt.monthプロパティを使うと日付から月を取得できます。
ここでは変数trainの中に新たに「month」という列を追加して、そこに月を入れています。
train[‘day’] = train[‘datetime’].dt.day
dt.dayプロパティを使うと日付から日を取得できます。
ここでは変数trainの中に新たに「day」という列を追加して、そこに日を入れています。
train = train.drop(‘datetime’, axis=1)
drop関数を使うと指定した行または列を削除できます。
ここでは「(‘datetime’, axis=1)」とすることで「datetime」の列を削除しています。行を削除する時は「axis=1」は不要です。

実行したあとhead関数でデータを確認すると次のようになります。

head関数 日付を数値型に変換後

「datetime」の列がなくなり、「year」「month」「day」の列が追加されています。また、info関数でデータ型を確認すると次のようになります。

info関数 日付を数値型に変換後

「year」「month」「day」が数値型(int64)として追加されています。

数値型を文字型に変換

次は数値型データを文字型に変換しましょう。下のコードを実行して下さい。

train['soldout'] = train['soldout'].astype(np.str) # soldout列を文字型に変換
train['payday'] = train['payday'].astype(np.str) # payday列を文字型に変換

コード解説

train[‘soldout’] = train[‘soldout’].astype(np.str)
astype関数を使ってデータの型を変更しています。
ここでは「astype(np.str)」として「soldout」列のデータ型を文字型に変換しています。
train[‘payday’] = train[‘payday’].astype(np.str)
上と同じです。ここでは「payday」列のデータ型を文字型に変換しています。

実行後、info関数でデータ型を確認すると次のようになります。

info関数 数値型を文字型に変換後

「soldout(完売日フラグ)」「payday(給料日フラグ)」のデータ型が文字型(object)に変わっています。

これでデータ型の変換も完了です。

機械学習手順2 データの基礎分析

ここではデータの基本的な性質を見る方法をご紹介します。

基礎統計量を確認する

まずは列ごとの基礎統計量(平均・最小・最大値など)を見てみましょう。「統計?…難しそう…」と構えなくても大丈夫です。関数がすべて計算してくれるので簡単に確認できます。

基礎統計量を知ることは、このあとに出てくるグラフを理解するのにも役立ちます。

数値型データの基礎統計量を確認する

下のコードを実行して下さい。

# データの基礎統計量を表示
train.describe()

コード解説

train.describe()
describe関数を使うと、列ごとの基礎統計量を計算して表示してくれます。数値型のデータ列のみが対象です。

実行結果は次のようになります。

describe関数 数値型データの基礎統計量

各行の意味は以下のようになっています。

項目意味備考
countデータの個数列にある空白ではないデータの個数
mean平均値列のデータの平均値
std標準偏差データの散らばり度。下で解説
min最小値列のデータで最も小さい値
25%第一四分位数。下で解説
50%中央値第二四分位数。下で解説
75%第三四分位数。下で解説
max最大値列のデータで最も大きい値

「y」列の場合、データの個数は207個、データの平均値は約86.6、最小値は29等と表示されています。

少しわかりにくい項目があると思いますので、説明を加えておきます。

標準偏差とは

標準偏差とは、データの散らばりを数値化したもので、数が小さいほどデータが密集していて、数が大きいほどデータが広い範囲に散らばっていることを表します。

例えばデータA・Bに以下のような数値が含まれている場合、平均値はどちらも4ですが、標準偏差はデータAが2、データBが4、とBの方が大きくなります。

データA「2,4,6」
データB「0,4,8」

下のコードを実行すると、実際にデータA・Bの基礎統計量が確認できます。気になる方はチェックしてみて下さい。

# データA・Bを作成して基礎統計量を確認する
df = pd.DataFrame({'A': [2, 4, 6], 'B': [0, 4, 8]})
df.describe()

中央値・四分位数とは

中央値とは、数字を小さいものから順に並べたときに真ん中にある数値です。

「2,3,4,5,6」の場合、中央値は4になります。
「2,3,4,5」の場合、中央値は3と4の平均で3.5になります。

また、四分位数とは、数字を小さいものから順に並べて4分割したときにそれぞれ区切りにある数値で、25%の所にある数値を第一四分位数、50%の所にある数値を第二四分位数(=中央値)、75%の所にある数値を第三四分位数といいます。100%ところにあるのは最大値になります。

下のコードを実行して最小値、25%、50%(中央値)、75%、最大値の5つの数値をチェックしてもらうと、その関係性が何となくイメージできると思います。

# データAを作成して基礎統計量を確認
df = pd.DataFrame({'A': [1, 2, 3,4,5,6,7,8,9]})
df.describe()

文字型データの基礎統計量を確認する

文字型データの基礎統計量も見ることができます。

下のコードを実行して下さい。

# 文字型データの基礎統計量を表示
train.describe(include=['O'])

コード解説

train.describe(include=[‘O’])
describe関数の()内に引数 「include=[‘O’]」(Oはゼロではなく英語のオー)を渡すと、文字型データ列ごとの基礎統計量を計算して表示してくれます。

実行結果は次のようになります。

describe関数 文字型データの基礎統計量

各行の意味は以下のようになっています。

項目意味備考
countデータの個数列にある空白ではないデータの個数
uniqueユニークデータの個数重複データを省いたデータの個数
top最頻値最も多く出現するデータ
freq最頻値の個数最も多く出現するデータの個数

上の結果からは、お弁当の日替わりメニューは207日間で156種類あり、最も多かったのは「メンチカツ」で207日間のうち6回登場したということが読み取れます。

ヒストグラム

ヒストグラムは量的データの分布を確認するのに利用します。ヒストグラムの形状からデータの性質をある程度把握できます。

ヒストグラムの形状と性質

形状データの性質
左右対称ヒストグラム 左右対称平均値・最頻値・中央値がほぼ同じ一般的なデータ。
(例:テストの点数、同学年同姓の身長・体重)
左右非対称ヒストグラム 左右非対称平均値・最頻値・中央値に乖離があるデータ。
(例:低所得が多く高所得少ない所得分布)
ふた山ヒストグラム ふた山データに複数の性質が違う集団が含まれている可能性がある。
(例:身長のデータに男子・女子が混ざっている)
離れ島ヒストグラム 離れ島外れ値がある
(例:例外、不良品データが混ざっている)

このように様々なことがヒストグラムから読み取れます。特に最後の外れ値には注意が必要です。外れ値の入ったデータで機械学習を行うと、予測結果が本来の結果からズレてしまうことがあるため、外れ値を除外するなどの対応が必要になります。

ヒストグラムを描く

それでは実際にヒストグラムを描いてみましょう。

下のコードを実行して下さい。

train['y'].plot.hist()

コード解説

ax = train[‘y’].plot(figsize=(10,4))
hist関数を使うことでヒストグラムを描けます。
ここでは変数train内の「y(販売数)」に関するヒストグラムを描いています。

実行結果は次のようになります。

お弁当販売個数のヒストグラム

左右非対称のヒストグラムが表示されました。1日の販売数が大体50個程度の日が最も多い(最頻値)ことがわかります。

ヒストグラムに平均値と中央値を重ねて描く

左右非対称のグラフの特徴は、平均値、最頻値、中央値に差があることでした。実際に平均値と中央値に線を引いて確認してみましょう。

下のコードを実行して下さい。

# ヒストグラムに平均値と中央値を重ねて描く
plt.axvline(x=train['y'].mean(), color='red') # 平均値を描く
plt.axvline(x=train['y'].median(), color='greenyellow') # 中央値を描く
ax = train['y'].plot.hist(figsize=(10,4)) # サイズ指定してヒストグラムを描く
ax.set_title('yのヒストグラム')
ax.set_xlabel('y')
ax.set_ylabel('分布量')

コード解説

plt.axvline(x=train[‘y’].mean(), color=’red’)
axvline関数を使うことで縦線を描けます。
ここでは「x=train[‘y’].mead()」としてxに「y(販売数)」の平均値を指定しています。
また「color=’red’」として線の色を赤色に設定しています。
plt.axvline(x=train[‘y’].median(), color=’greenyellow’)
ここでもaxvline関数を使って縦線を描いています。
ここでは「x=train[‘y’].median()」としてxに「y(販売数)」の中央値を指定しています。
また「color=’greenyellow’」として線の色を黄緑色に設定しています。
ax = train[‘y’].plot.hist(figsize=(10,4))
横10インチ、縦4インチのサイズでヒストグラムを描いてます。
ax.set_title(‘yのヒストグラム’)
ヒストグラムに「yのヒストグラム」というタイトルを付けています。
ax.set_xlabel(‘y’)
X軸に「y」というラベルを付けています。
ax.set_ylabel(‘分布量’)
Y軸に「分布量」というラベルを付けています。

実行結果は次のようになります。

織部島販売個数のヒストグラムと中央値、平均値

最頻値、中央値(黄緑線)、平均値(赤線)に差があることがわかります。この例のように平均値だけではデータの全容はわかりません。データを可視化して自分の目で確かめることが大切です。

機械学習手順3 有効な説明変数の選定

ここでは「y(販売数)」を予測するためにはどの説明変数(特徴量)を使って学習するのが有効だと考えられるかを探っていきます。

データの変化を可視化する

まず目的変数(予測対象)となる「y(販売数)」がどのように変化しているか確認してみましょう。

折れ線グラフ

折れ線グラフはデータの変化を確認するのに便利なグラフです。

折れ線グラフを描く

「y(販売数)」を折れ線グラフとして描画しましょう。下のコードを実行して下さい。

# 折れ線グラフの描画
train['y'].plot()

コード解説

train[‘y’].plot()
plot関数を使うと折れ線グラフを描けます。
ここでは変数train内にある「y(販売数)」のデータを折れ線グラフにしています。

実行結果は次のようになります。

折れ線グラフ

このように簡単に折れ線グラフが描けます。

折れ線グラフを見やすく描く

折れ線グラフは描けましたが、タイトル付けや、サイズ調整をしてもう少し見やすくしてみましょう。下のコードを実行して下さい。

# 折れ線グラフを描く
ax = train['y'].plot(figsize=(10,4))
ax.set_title('yの折れ線グラフ')
ax.set_xlabel('時間')
ax.set_ylabel('y')

コード解説

ax = train[‘y’].plot(figsize=(10,4))
先程と同じく販売数に関する折れ線グラフを描いています。
ここでは「figsize=(10,4)」とオプションを指定することで、グラフのサイズを横10インチ、縦4インチに設定しています。
また、このあとタイトルなどの設定をするため、グラフを変数axの中に格納しています。
ax.set_title(‘yの折れ線グラフ’)
set_title関数を使うと折れ線グラフにタイトルを設定できます。
ここでは「yの折れ線グラフ」というタイトルを付けています。
ax.set_xlabel(‘時間’)
set_xlabel関数を使うとX軸にラベルを設定できます。
ここでは「時間」というラベルを付けています。
ax.set_ylabel(‘y’)
set_ylabel関数を使うとY軸にラベルを設定できます。
ここでは「y」というラベルを付けています。

実行結果は次のようになります。

折れ線グラフを見やすく描く

先程より少し見やすくなりましたね。グラフ化したことで、お弁当の販売数が時間の経過とともに少なくなってきているのがわかるかと思います。このことから「datetime(日付)」と「y(販売数)」には関連性があると考えられます。

また、販売数が減ってきてもトゲのように突出した箇所があり以前と変わらず売れている日もあるようです。

量的データ同士の相関関係を確認する

次は「y(販売数)」とそれ以外のデータの相関関係を見てみましょう。相関とは、あるデータ同士、例えば「データA」と「データB」の関連性のことです。

データの関係相関備考
Aが増えるとBも増える相関あり正の相関
Aが増えるとBは減る相関あり負の相関
Aが増えるとBはランダムに増減相関なし

表の1行目と2行目のように「データA」の変化が「データB」の変化と関連性があることを「相関あり」といいます。

相関係数

相関係数とは、データ同士の相関を数値化したもので、-1?1の間の数値で表されます。

相関係数-1
相関相関あり
(負の相関)
相関なし相関あり
(正の相関)

表のように「1」または「-1」に近いほど相関が強く「0」に近いほど相関は少ないことを表します。

相関係数を確認する

Pythonでは数値型データ同士の相関係数を簡単に確認できます。下のコードを実行して下さい。

# 相関係数の表示
train.corr()

コード解説

train.corr()
corr関数を使うと数値型データ同士の相関係数を表示できます。
ここでは変数train内に含まれる数値型データの全ての組み合わせの相関係数を表示しています。

実行結果は次のようになります。

corr関数で相関係数を表示

「y(販売数)」との相関を知りたいので、「y」列に注目しましょう。「temperature(天気)」が約-0.6、「year(年)」が約-0.5と「y」と相関が高いことを確認できます。
(※「1」となっている箇所は「y」と「y」など同じデータの相関係数で、同じデータなので当然相関は最大の1となります)

ヒートマップ

ヒートマップを使うと損関係数を視覚的に把握できます。下のコードを実行して下さい。

df_corr = train.corr() # 相関係数を取得
sns.heatmap(df_corr) # ヒートマップを描く

コード解説

df_corr = train_new.corr()
corr関数を使って変数train内のデータ同士の相関係数を取得し、変数df_corrに入れています。
sns.heatmap(df_corr, cmap=’Blues’)
heatmap関数を使うとヒートマップを描けます。
ここでは「(df_corr)」とすることで変数df_corrに入れた相関係数をヒートマップ化しています。

実行結果は次のようになります。

相関係数をヒートマップで表示

黒い箇所と白い箇所が相関が高いところです。先ほど見た「temperature」と「year」が黒くなっています。今回のケースでは必須というわけではありませんが、データ数が多くなるとヒートマップが便利です。

質的データと量的データの相関関係を確認する

次は質的データと量的データの相関を見てみましょう。

箱ひげ図

箱ひげ図を使うと、質的データと量的データの関係を確認できます。

あまり聞き慣れない箱ひげ図ですが、実は結構シンプルで最小値・四分位数・最大値を図にしたものです。(前述:中央値・四分位数とは

箱ひげ図 概要

上のように、箱の部分には上・下25%を除いた中心から50%のデータが含まれています。

また箱ひげ図は高さが低いほど、データが密集していることを表しています。

箱ひげ図 データの分布密度の見方

この図では、50~60くらいの値を持つデータが最も多いことがわかります。

また、箱ひげ図でもヒストグラムと同じように外れ値を確認できます。

箱ひげ図 外れ値

外れ値があると上のように小さな点として表示されます。

箱ひげ図での相関関係の見方

質的データの区分ごとに量的データの分布を表示することで、質的データと量的データの間に相関があるかどうか視覚的にチェックできます。

相関がある箱ひげ図

相関のある箱ひげ図

このように区分ごとに量的データの分布が異なる場合、この質的データと量的データの間には何らかの関連性があると考えられます。

相関がない箱ひげ図

相関のない箱ひげ図

このように区分に分けても量的データの分布に差がない場合、この質的データと量的データの間には関連性がないと考えられます。

箱ひげ図を描く

それでは箱ひげ図を描いてみましょう。下のコードを実行して下さい。

train[['y', 'week']].boxplot(by='week')

コード解説

train[[‘y’, ‘week’]].boxplot(by=’week’)
boxplot関数を使うことで箱ひげ図を描けます。
ここでは「y(販売数)」と「week(曜日)」の関係性を箱ひげ図にしています。
by=’week’」を指定することで、曜日別の販売数の分布を表示します。

実行結果は次のようになります。

箱ひげ図 曜日ごとのお弁当販売個数

月曜日は販売数が多い、木曜日は少ないなどある程度の関連性があることが見て取れるのではないでしょうか。

同じ要領で他の質的データとの相関も見てみましょう。下のコードを実行して下さい。

objcols = ['week','soldout','name','remarks','event','payday','weather']
for objcol in objcols:
  train[['y', objcol]].boxplot(by=objcol)

コード解説

objcols = [‘week’,’soldout’,’name’,’remarks’,’event’,’payday’,’weather’]
for objcol in objcols:
train[[‘y’, objcol]].boxplot(by=objcol)
上と同じくboxplot関数で箱ひげ図を描いています。
こちらではobjcolsという名前で質的データの列名が入ったリストを作り、それをfor文でループ処理することでまとめてグラフを出力しています。

実行結果は次のようになります。

箱ひげ図 完売フラグ

「soldout(完売フラグ)」からは、完売した日(1)は販売数が多いことが読み取れます。ただし完売したかどうかわかるのはその日の営業終了後なので、販売数の予測に完売フラグを利用するのは無理がある気がします。

箱ひげ図 メニュー

「name(メニュー名)」は種類が多すぎてこの箱ひげ図から情報を読み取るのは難しいです。

箱ひげ図 特記事項

下の項目名が見にくいですが、「remarks(特記事項)」に「お楽しみメニュー」がある日は販売数が多いようです。

箱ひげ図 イベント

「event(イベント)」を見ると、キャリアアップ支援セミナーがある時は販売数が落ちるようです。

箱ひげ図 給料日

「payday(給料日)」からは、給料日の時(1.0)にはお弁当の販売数がやや多い傾向にあることが読み取れます。

箱ひげ図 天気

「weather(天気)」からは「快晴」の日は売れ行きが良く、「薄曇」の日は売れ行きが良くないことなどが読み取れます。

機械学習手順4 学習モデルと評価関数の選定

使用する説明変数(特徴量)の目星がついたら、次は学習モデルと評価関数を決めましょう。

学習モデル

機械学習といっても学習モデルには様々なものがあります。それぞれ得意・不得意があり万能なモデルは今のところ存在しないため、自分の持つ課題にはどのモデルが適しているか試していくことが大切です。

モデル系列モデル予測対象特徴
回帰線形回帰数値回帰によって数値を予測
ロジスティック回帰分類回帰によって分類する
サポートベクターマシーン(SVM)分類回帰によって分類する
ツリー決定木分類木構造モデルによって分類
回帰木数値回帰と決定木の組み合わせモデル
ランダムフォレスト数値・分類決定木を複数作って集計
勾配ブースティング木数値・分類決定木を複数作って集計
ニューラルネットワーク
(NN)
パーセプトロン数値・分類入力、中間、出力の3層からなるNN
畳み込みニューラルネットワーク(CNN)数値・分類画像解析が得意なNN
再起型ニューラルネットワーク(RNN)数値・分類文章解析が得意なNN
残差ネットワーク数値・分類CNNの発展系
ベイズ単純ベイズ分類文章の単語分類が得意
時系列AR、MR、ARIMA数値予測対象の過去値から予測
状態空間モデル数値予測対象以外の過去値から予測

今回の予測対象は「y(販売数)」という数値なので、数値を予測できるモデルとして線形回帰モデルを選択します。

評価関数

評価関数は、作成した学習モデルによって出された予測結果がどれだけ正しいかを評価する関数です。

対象評価関数評価方法特徴
数値MAE(平均絶対誤差)0~∞小さい程良い正解の回数が多いほど高く評価。
RMSE(平均平方二乗誤差)0~∞小さい程良い誤差が大きいほどペナルティを与える。誤差を少なくしたい時に使用。
分類Accuracy0~1大きい程良い解釈しやすいが、データ数のバランスが悪いと評価がズレる。
AUC0~1大きい程良い2値分類問題で使用。データ数のバランスが悪くても上手く評価。
LogLoss0~∞小さい程良い多値分類問題でも使用可能。

今回は数値の予測結果を評価し、なるべく誤差を少なくしたいことからRMAEという評価関数を使用します。

機械学習手順5 過学習の防止

過学習とは、モデルが学習データに過剰適用してしまうことです。

モデルが過学習になると、学習データに対する予測精度は非常に高い反面、実際に予測したい未知のデータに対する予測精度は低くなってしまいます。

ここでは過学習を防ぐために擬似的に未知のデータを作成します。

学習データから擬似的に未知データを作成

学習データでモデルを作成したあと、未知のデータを使って予測を行い評価することで、未知のデータに対する予測精度を測ります。

擬似的に未知のデータを作成する

それでは学習データ「train」を分割して評価用データ(未知のデータ)を作成しましょう。今回は学習データ80%、評価データ20%程度になるように分割します。下のコードを実行して下さい。

# 学習データを学習用と評価用に分割
train1 = train[:165] # 学習用データ
train2 = train[165:] # 評価用データ

コード解説

train1 = train[:165]
train[a:b]でtrainのa行目~b行目までを取得できます。
ここではtrain1という新しい変数にtrainの最初から164行目までのデータを入れています。
train2 = train[165:]
ここではtrain2という新しい変数にtrainの165行目から最後までのデータを入れています。

train1の末尾5行、train2の先頭5行をそれぞれ確認してみましょう。

tail関数 分割した学習データの末尾を見る

tail関数 分割した評価用データの先頭を見る

train1には164行目までの2004年7月25日までのデータが、train2には165行目以降の2004年7月28日以降のデータが入っているのが確認できます。

機械学習手順6 モデルの作成・予測・評価

モデルに合わせたデータの準備

今回作成する線形回帰モデルは「教師あり」学習といわれるもので、学習するためには「説明変数(y以外)」と「目的変数(y:販売数)」の2つが必要です。

学習用データtrain1を説明変数と目的変数に分割しましょう。下のコードを実行して下さい。

# train1_Xに説明変数、train1_Yに目的変数を入れる
train1_X = pd.get_dummies(train1[['year','month','temperature','week']])
train1_Y = train1['y']

コード解説

train1_X = pd.get_dummies(train1[[‘year’,’month’,’temperature’,’week’]])
train1の中から「year(年)」「month(月)」「temperature(気温)」「week(曜日)」の列を取り出して、新たな変数train1_Xの中に入れています。
またget_dummies関数を使って文字型データをダミー変数化しています。
train1_Y = train1[‘y’]
train1の中から「y(販売数)」の列だけを取り出して、新たな変数train1_Yの中に入れています。

ダミー変数化とは

ダミー変数化とは、文字型データを数値型データに変換することです。

今回使う線形回帰モデルは、説明変数・目的変数ともに数値型データである必要がありますが、今回は文字型データである「week(曜日)」も説明変数として使いたいので、この「week」をダミー変数化する必要があります。

ダミー変数化前

「week」には上のように曜日が文字型データで入っています。

ダミー変数化後

ダミー変数化すると、上のように月~金それぞれの列が新しく作成され、該当するデータにのみ「1」が設定されます。


それでは先程作成したtrain1_X、train1_Yの中身をhead関数で確認してみましょう。

train1_X 説明変数

train1_Y 目的変数

train1_Xには「year」「month」「temperature」とダミー変数化された「week」が、train1_Yには「y」が入っているのが確認できます。

評価用データtrain2も全く同じように準備しましょう。下のコードを実行して下さい。

# train2_Xに説明変数、train2_Yに目的変数を入れる
train2_X = pd.get_dummies(train2[['year','month','temperature','week']])
train2_Y = train2['y']

これで線形回帰モデルに使うデータの準備ができました。

モデルの作成・学習

それではいよいよモデルを作成します。下のコードを実行して下さい。

model = LR() # 線形回帰モデルを作成
model.fit(train1_X, train1_Y) # モデルに学習させる

コード解説

model = LR()
先のセットアップの項目で読み込んだ線形回帰モデル作成モジュール「LinearRegression(LR)」を使ってモデルを作成しています。
model.fit(train1_X, train1_Y)
fit関数を使ってモデルにデータを学習させています。
ここでは「(train1_X, train1_Y)」として説明変数に「train1_X」を、目的変数に「train1_Y」を指定しています。

これで学習済みモデルができました。

モデルから予測する

それでは作成したモデルを使って予測してみましょう。下のコードを実行して下さい。

# 学習済みモデルで予測する
train1_P = model.predict(train1_X) # 学習用データから予測
train2_P = model.predict(train2_X) # 評価用データから予測

コード解説

train1_P = model.predict(train1_X)
predict関数を使うと予測データを取得できます。
ここでは「(train1_X)」として既知の学習用データを目的変数として渡し、新しい変数train1_Pの中に予測結果を入れています。
train2_P = model.predict(train2_X)
上と同じですが、こちらは「(train2_X)」として未知の評価用データを目的変数として渡し、新しい変数train2_Pの中に予測結果を入れています。

予測結果(train1_Pまたはtrain2_P)を見てみましょう。下のコードを実行して下さい。

print(train2_P) # 予測結果を表示

コード解説

print(train2_P)
print関数を使うとデータの中身を表示できます。
ここでは「(train2_P)」として評価用データからの予測結果を表示しています。

実行結果は次のようになります。

未知のデータに対する予測内容を表示

このようにtrain2_Pにはモデルが予測した日々の「y(販売数)」が入っています。

予測結果を評価する

それでは予測したデータの精度を確認してみましょう。下のコードを実行して下さい。

# 評価する
train1_R = np.sqrt(mean_squared_error(train1_Y, train1_P))
train2_R = np.sqrt(mean_squared_error(train2_Y, train2_P))

print('予測誤差/学習用データ:{}、評価用データ:{}'.format(train1_R, train2_R))

コード解説

train1_R = np.sqrt(mean_squared_error(train1_Y, train1_P))
ここでは既知のデータの実測値(train1_Y)と予測値(train1_P)の誤差の平均値を取得しています。
mean_squared_error関数を使ってMSE(平均二乗誤差)を取得し、sqrt関数で平方根を取ることでRMSE(平均平方二乗誤差)を取得しています)
train2_R = np.sqrt(mean_squared_error(train2_Y, train2_P))
ここでは未知のデータの実測値(train2_Y)と予測値(train2_P)の誤差の平均値を取得しています。
print(‘予測誤差/学習用データ:{}、評価用データ:{}’.format(train1_R, train2_R))
print関数を使うとデータを表示できます。また、format関数を使うと文字の中に数値を埋め込めます。ここでは「{}」の部分ににtrain1_R、train2_Rを表示させています。

実行結果は次のようになります。

予測結果の評価 RMSE

学習用データ(既知データ)での予測誤差は約19個、評価用データ(未知データ)での予測誤差は約20個となりました。既知データと未知データで予測精度にほぼ差がないため、過学習の心配はなさそうです。

機械学習手順⑥ モデルの予測精度を高める

誤差約20個という予測結果が出ましたが、もう少し予測精度を上げてみましょう。

誤差の多いデータを見つける

train1・train2に予測結果を付け足して実測値(y)との差を求めましょう。誤差が大きいデータを見つけて何らかの対応が見つかれば、更に精度を上げられます。下のコードを実行して下さい。

train1 = train1.assign(prev=train1_P) # 予測の列を追加
train2 = train2.assign(prev=train2_P) # 予測の列を追加

train1['diff'] = abs(train1['y'] - train1['prev']) # 誤差の列を追加
train2['diff'] = abs(train2['y'] - train2['prev']) # 誤差の列を追加

trainW = pd.concat([train1, train2]) # train1とtrain2を結合
trainW.sort_values('diff', ascending=False)[:10] # 誤差の多い順にソート

コード解説

train1 = train1.assign(prev=train1_P)
train2 = train2.assign(prev=train2_P)
assign関数を使うと新たな列を追加できます。
ここでは「(prev=train1_P)」として、「prev」という名前の列を新しく追加し、そこにtrain1_Pの予測データを入れています。
(2行目の評価用データtrain2についても同じです)
train1[‘diff’] = abs(train1[‘y’] – train1[‘prev’])
train2[‘diff’] = abs(train2[‘y’] – train2[‘prev’])
「y(販売数)」から「prev(予測販売数)」を引いた値を「diff」という新しい列を作成してそこに入れています。また引いた値はasb関数を使ってマイナスを消した絶対値に変換しています。
(2行目の評価用データtrain2についても同じです)
trainW = pd.concat([train1, train2])
concat関数を使うとデータを結合できます。
ここではtrain1とtrain2を結合してtrainWというデータを作成しています。
trainW.sort_values(‘diff’, ascending=False)[:10]
sort_values関数を使うとデータを並べ替えて表示できます。
ここでは「(‘diff’, ascending=False)」として「diff」の値を降順でソートしています。また「[:10]」として先頭10行だけを表示しています。つまり誤差の最も大きいもの順に10個を表示しています。

実行結果は次のようになります。

予測と実測値の誤差が大きいものからソートして表示

「diff(誤差)」の大きいデータには「remarks(特記事項)」に「お楽しみメニュー」と書かれているものが多いことがわかります。

説明変数(特徴量)を作る

それでは「remarks(特記事項)」と「y(販売数)」の相関関係を再度確認してみましょう。

箱ひげ図 特記事項

先の項目で描いた「remarks(特記事項)」の箱ひげ図です。これを見ると一番左の「お楽しみメニュー」の50%が販売数100~120であること、左から二番目の「なし」では50%が販売数60~110であることから、「お楽しみメニュー」があるときは販売数が多くなる傾向が強いことが見て取れます。また、それより右の項目では箱ひげ図がキレイに描かれておらず、これはデータがほとんどないことを意味しています。

以上のことから、「お楽しみメニュー」がある日を「1」、それ以外の日を「0」とした新しい説明変数(特徴量)を作ってみましょう。下のコードを実行して下さい。

# ユーザー定義関数
def is_otanoshimi_funcion(x):
	if x == 'お楽しみメニュー':
		return 1
	else:
		return 0

# お楽しみメニューをフラグ化
train['fun'] = train['remarks'].apply(lambda x: is_otanoshimi_funcion(x))
train['fun'] = train['fun'].astype(np.str) # fun列を文字型に変換

コード解説

def is_otanoshimi_funcion(x):
if x == ‘お楽しみメニュー’:
return 1
else:
return 0
ここではis_otanoshimi_funcionという関数を定義しています。
引数「x」が「お楽しみメニュー」なら「1」を返し、それ以外なら「0」を返すだけの単純な関数です。
train[‘fun’] = train[‘remarks’].apply(lamda x: is_otanoshimi_funcion(x))
apply関数を使うと、データ1つ1つに対して関数を実行できます。
ここでは上で定義したis_otanoshimi_funcionという関数に「remarks」の値を渡し、返ってきた値を新しく作成した列「fun」に入れています。
train[‘fun’] = train[‘fun’].astype(np.str)
astype関数を使って「fun」列を文字型に変換しています。

中身を確認してみましょう。下のコードを実行して下さい。

train[train['remarks']=='お楽しみメニュー'].head()

コード解説

train[train[‘remarks’]==’お楽しみメニュー’].head()
head関数を使って先頭5行を表示しています。
ここでは「train[train[‘remarks’]==’お楽しみメニュー’]」として、「remarks」に「お楽しみメニュー」と設定されている行だけを表示しています。

実行結果は次のようになります。

新しい特徴量を作成

「remarks」に「お楽しみメニュー」がある行の「fun」に「1」が設定されています。これで新しい説明変数を作成できました。

モデルの再作成・予測・再評価

それでは先にモデルを作成した時に使った説明変数「year、month、week、temperature」に「fun」を加え、もう一度モデル作成→予測→評価をしてみましょう。下のコードを実行して下さい。

# 学習データを学習用と評価用に分割
train1 = train[:165]
train2 = train[165:]

# 説明変数と目的変数の抽出
train1_X = pd.get_dummies(train1[['year','month','temperature','week','fun']])
train1_Y = train1['y']
train2_X = pd.get_dummies(train2[['year','month','temperature','week','fun']])
train2_Y = train2['y']

# モデルの元を作成
model = LR()
# 重回帰モデルを作成
model.fit(train1_X, train1_Y) #説明変数, 目的変数

# 学習データから予測する
train1_P = model.predict(train1_X) # 学習用データで予測
train2_P = model.predict(train2_X) # 評価用データで予測

# 評価する
train1_R = np.sqrt(mean_squared_error(train1_Y, train1_P))
train2_R = np.sqrt(mean_squared_error(train2_Y, train2_P))
print('予測誤差/学習用データ:{}、評価用データ:{}'.format(train1_R, train2_R))

実行結果は次のようになります。

特徴量作成後のモデルの予測結果評価

学習用データでの誤差が約19→16、評価用データでの誤差は約20→14と、ともに予測精度が向上しました。

まとめ

最後まで読み進めて頂きありがとうございます。そしてお疲れ様でした!

機械学習の流れは理解できたでしょうか。機械学習=モデル作成というイメージですが、実際は多くの時間をデータの準備に費やしていることを感じられたと思います。

機械学習はデータ選択やモデル選択によって予測結果が大きく変わります。ここで挙げた流れも一例で、学習に使うデータを少し変えたり採用する学習モデルを変えるだけでも予測精度を上げることができます。

誤記・間違い、わからない箇所、上手くいかない箇所、または、上手くいった!もっといい方法があるよ!などなどありましたら下のコメント欄からお気軽にコメント頂けると幸いです。

コメント

タイトルとURLをコピーしました