こちらの記事をご覧いただきありがとうございます。
どのあたりのレベルまでを初心者って言うんですかね…?
しばらく前からSUUMOの賃貸データをいろいろ分析したりアプリにしたりしていました。
私のやりたいことはやれたと思うので、やったことをここに簡単にまとめておきます。
自分のまとめ用に書いたので見づらいところもあると思いますが、ご覧になった人に何か学びがあれば幸いです。
Gitのコードも公開しておきますので、私の汚いコードでよろしければぜひ参考にしてください。
機械学習モデルを用いた分析
アプリ構築
SUUMO分析の目的
アプリ構築までの過程を通して、データ収集・分析・モデル構築・アプリ設計と構築の技術を身に付ける+考え方を理解する
目標
家賃を予測してくれるアプリを作ること
また過程を以下とすること
・スクレイピング
・前処理
・データ分析
・モデルによる家賃予測
・家賃予測モデルを利用したアプリを構築
やったこと
スクレイピングで都内23区の物件データを収集
件数:222923件
特徴量:16種
工夫:
- 処理時間、進捗、終了目安を表示
- 作業完了の度に音を出して他作業も並行しやすくした
- 掲載情報と照らし合わせが出来るようにurlを残した
学び:
- サイト構造を意識したスクレイピングが出来るようになった
- 処理時間を意識するようになった
反省:
- 最初とはいえコードが割と汚かった気がする(後で再利用した時に使いづらかった)ので構造をわかりやすくしたりコメントを残したりで解消する。
詳細記事
正規表現を使用して前処理 + 変数の追加
特徴量:16種→33種
工夫:
- 加工する前にドメイン知識を確認して適切な処理を検討した
- 正規表現の処理は複数の特徴量に適応できるものは関数にして使いまわした
- 様々な検証が出来るように変数を様々に用意した
- 処理前後で情報量が変わらないように変数を分割した
- 住所→緯度経度に変換するAPIを利用して緯度経度変数を生成した
- 緯度経度を利用して皇居からの距離(都心からの離れ具合)と最寄駅からの距離の変数を生成した
- 重複するデータは削除した
学び:
- 情報量への意識が高まった
- 目的に合わせた変数を考慮するようになった
- APIの使い方を理解した
- 例外処理の書き方を学んだ
反省:
- 結局予測に利用しなかったものまで余計にスクレイピングしたりしたので、目的をハッキリさせて後にやることをわかりやすくした方がいい。
詳細記事
記述統計による分析
代表値
相関行列
ヒストグラム
散布図
箱ひげ図
分かったこと(意外だったもののみ):
- 物件は15階より大きいものは稀
- 徒歩10分以内と以上で数に差がある
- 築年数のヒストグラムに周期性を感じる(原因不明)
- 建物の構造は建物種別によって使い分けがある
工夫:
- 変数名だけ書き直せば他の変数で利用できるようにコードを記述
- 見たいものを見やすく出力した
- urlから掲載情報と照らし合わせてデータを確認
学び:
- 見たいものを形にするコードの書き方が身についた(特にplt)
- 取りまわす前提のコード記述を意識した
- 見やすさを意識したグラフの出力をした
- 丁寧に調べるほどデータの粗が見つかることを感じた
反省:
- 知りたいことをその場でコードを書き始めて調査していたのでコードが汚くなった
- その場限りならすぐ消した方がいいし、いくらか利用するものなら配置を考えたほうがいい
(その後は変数ごとにブロックを作った)
詳細(?)記事
線形回帰モデルによる家賃予測
サンプル数:156943
目的変数:家賃
特徴量:32種 (質的変数のOneHotにより最大624種)から取捨選択
最終着地
特徴量:17種(量的変数は標準化、カテゴリ変数はOneHot)
目的変数の家賃は対数に変換
入力ミスと思われるデータを削除
学習:テスト=10000:146940
R2スコア:0.752
RMSE:16.65
工夫:
- サンプルに偏りがないか確認した
- 家賃を対数変換した
- 入力ミスらしいデータを削除した
- 比較検証のために行ったことは上書きせずそのまま残した
学び:
- データを整備していないと正しく予測できないのでデータ整備の確認に役に立った
- 解釈のしやすさはあるもののシンプルな表現であるため複雑な構造の再現が出来ず大雑把な傾向の把握にとどまった
- サンプルが少ない属性が予測結果を破壊することから細かい分割が必ずしも適切ではないことを理解した
- 使いまわしやすいコードは比較検証にも役に立った
反省:
- 解釈性の高さや軽さの面では優秀かも知らんが、軽さならLightGBMでも問題ないし、
解釈性なら複雑な状況を再現できなくて結局得るものがなかったりするので、別になくてもいいかもしれない
変なデータの検出には使えるかも
LightGBMによる家賃予測
サンプル数:156943
目的変数:家賃
特徴量:22種から取捨選択
ベースライン:
LightGBMパラメータ調整なし
特徴量:22種(カテゴリ変数はラベルエンコーディング)
学習:テスト=1:9
R2スコア:0.800
RMSE:13.60
最終着地:
LightGBMパラメータ調整:
n_estimators=1000
num_leaves=1400
max_depth=70
colsample_bytree=0.85
learning_rate = 0.1
特徴量:8種(カテゴリ変数はラベルエンコーディング)
目的変数の家賃を対数に変換
学習:テスト=5:5
R2スコア:0.902
RMSE:6.83
工夫:
- 残差が大きいサンプルの特徴量から推測がうまく行かない原因を推定→賃料が高い物件がうまく行っていないことが判明
- オプション情報など詳細な情報の追加により改善を試みる。
学び:
- 計算に時間がかかるときの時間の使い方を考えるようになった
(後に行うことの準備、下調べ、後の勉強 など) - LightGBMはかなり使いこなせるようになった気がする
反省:
- 線形回帰の再利用も反省の活用もあったのでそんなに悪くないのでは?たぶん
詳細(?)記事
賃貸の詳細情報の取得
物件個別にある詳細情報のページから各物件の情報を取得。
件数:196093
特徴量:29種
工夫:
- 最初から最後までを一気に行うコードだとリスクが大きいので工程ごとに分割されたコードにした
- アクセス数がかなり増えて相当時間がかかるので中断と再開を前提としたコードにした
学び:
- 工程の分割はリスク分散になることを実感した
- コードを後で利用しやすい形で残しておくと便利
反省:
- 不要だと判断できる情報も処理していたので、最初から捨ててしまえば時間の削減になった
- スクレイピングした直後の状態での保存を忘れていたので加工しなおしが一部不可能になった(今回は特別不都合はなかった)
- 重複するデータの削除を忘れていた
新規データの前処理と記述統計
前述とほぼ同じなので割愛。
記述統計の傾向もほぼ変わらなかった。
学び:
- 残しておいたコードが役に立った
詳細情報を含めてLightGBMで家賃予測
サンプル数:196093
目的変数:家賃
特徴量:30種から取捨選択
ベースライン:
LightGBMパラメータ調整なし
学習:テスト=1:9
R2スコア:0.870
RMSE:7.98
最終着地:
LightGBM
n_estimators = 1000,
learning_rate = 0.1,
特徴量:12種
学習:テスト=7:3
R2スコア:0.962
RMSE:2.58
工夫:
- ↑とやったことはだいたい同じ。ほぼほぼコードの使いまわしで完結した
学び:
- ほぼほぼ一度行ったことの繰り返しだったので大半はコードの使いまわしで完結した
見やすいコードを書く重要性を実感した
反省:
- パラメータ調整してもしなくてもあまり変わらなかった点は要因不明なのでもしかしたらもっと精度向上が望めたかもしれない
- 詳細情報と比較して差が出たのは重複データの影響もあり得る?
詳細(?)記事
オプションの与える影響調査
↑の最終着地モデルにオプション有無の変数を追加して影響がどの程度かを調べる
分かったこと:
- 予測精度が上がったオプション→高額帯の物件についていない(敷金礼金不要など)
- 変数重要度が高いオプション→あるとないで家賃平均に差がある(宅配ボックスなど)
- 予測精度が上がるとしても0.03程度なので大きな差とはならなかった。
工夫:
- 自動でオプション100種それぞれを追加したパターンの予測精度を計算して結果出力まで行えるコードを書いた
学び:
- 情報の多さがすべてではないと感じた。詳しすぎは過学習の原因。
- スパースなデータの取り扱いが大変だった。
反省:
- 今回はあまり必要なかったけどスパースデータの扱い方までよく理解できると分析の幅が広がったかもしれない。
詳細記事
ニューラルネットワークを用いた家賃予測
サンプル数:196093
目的変数:家賃
特徴量:30種から取捨選択
ライブラリはtensorflow
最終着地
特徴量:9種(量的変数は標準化、質的変数はEmbedding層で2列に変換)
家賃は対数変換
学習:テスト=7:3
層:11×150×150×150
learning_rate=0.001
batch_size=100
R2スコア:0.934
RMSE:4.41
工夫:
- 層やニューロンの追加に加え、ドロップアウトやL2正則化で過学習への対策など試みた(そんなにうまく行かなかった)
学び:
- ニューラルネットを使った予測の基礎を理解できた気がする
反省:
- パラメータチューニングの進め方は検索で調べながら行ったがうまく行かなかった印象なので、次の機会にはより詳しく知識を身に付けてから取り組みたい
SHAPを用いたモデル予測とデータの性質の分析
使用したモデル:
詳細情報を利用したLightGBMの時と同じ
分かったこと(意外だったことのみ):
- 面積が広いほど家賃が高いが、120m2以上になるとさほど影響しなくなる
- 築年数による家賃の現象は30年で緩やかになり50年以降は大きく変わらなくなる
- 建物が高いほど高額だが30階以上は分散が広くなって傾向が感じられなくなる
- 区の家賃への影響力が、面積が広いほうがいいところと狭いほうがいいところがある
(面積が広いほうが家賃が高くなる傾向は変わらないので結局広いほうが家賃が高い。)
→面積による家賃の伸びが区によって変わる
工夫したこと:
- 使いまわしは大事
- 表示の仕方を変えて見やすくした。特にSHAP値の配列をうまく活用して表示しなおした
学び:
- SHAPを使用した分析の見方や解釈の仕方がよく理解できた
- 変数同士の関係性の考察の考え方が身についた
- 相互作用は変数×変数だけパターンがあるがわかりやすいものから注目して時間のかかりすぎを防いだ
反省:
- 実は記述統計をよく眺めていれば分かったことも多かったかもしれない。
- 家賃と特徴量の関係に注目しがちだったので、他の変数同士の関係も注目すると共線性とか防ぎやすくなるかも
詳細記事
賃貸条件から家賃を予測するアプリ
フレームワーク:flask
環境:heroku → AWS Elastic Beanstalks(以下eb)
フォームから家賃の条件を入力
→学習済みモデルが家賃の予測値を計算
→結果を出力
工夫:
- なるべく役割ごとにファイルを分けて記述、
- 対話型で挙動の確認→ローカル環境で実装し挙動を確認→デプロイとデータの動きをよく確認しながら実装したので、想定通りに動かないパターンはほとんどなかった
- レスポンスもさほど時間がかからないので動きは悪くないように思う。
学び:
- データの動きで想定外はほとんどなかったが、flaskやheroku、ebの仕様がよくわからずに苦労する場面は多かった
- 様々なサイトから実践例を調べて試したので、仕様はある程度理解できた
- エラー対処の考え方が身についた
反省:
- 動きを作ることを目標として作成したのでCSSを利用した見た目の調整やUIに配慮した設計はしていない(CSSの勉強をしていないとも言う)
次の機会にはユーザの使いやすさに配慮したものを作りたい - 自分のやりたい形を実現しようとした結果かえって時間がかかっていた節があった
公式のガイドや他実践例をひとまずそのまま動かしてそれをうまく書き換える方針だと引っかかる場面は少なかったかもしれない - コードの書き方が散らかっているように思うので、整理されたコードで書けるようにしたい
- コードの中に日本語は使わない方がいい
詳細記事
賃貸条件から類似する賃貸を推薦するアプリ
フレームワーク:flask
環境:heroku → AWS Elastic Beanstalks(以下eb)
フォームから賃貸の条件を入力
→データベース内の物件情報とコサイン類似度を計算
→コサイン類似度の高いもの3件を出力
工夫:学び:反省:
- ↑とだいたい同じ。
- エラーの原因が計算機の都合だった(inverse_transform時に小数第??桁でずれた)ので、そういうこともあるのかと勉強になった
- レコメンドシステムを簡単なものでいいから作れないかと考えた結果かなり簡素な形になった。
協調フィルタリング等を勉強して、いずれユーザの行動履歴からアイテムをレコメンドするシステムを構築したい。
詳細記事
全体を振り返った学びと反省
SUUMO分析やアプリ開発をする中で感じたことを述べます。
だいたいはどこでも言われているようなことだと思います。どこでも言われているようなことだからこそ、大事なんじゃないですかね?たぶん。
目的をハッキリさせておくと良い
SUUMO分析を始めるときに、目的を分析とアプリ構築をデータ収集から自力で行って一連の活動に対する理解を深めることと決めていました。
そのおかげで勝つ等を進める中でやることに迷ったことはほとんどなかったと思います。
逆に、他のやってみたいことに目がくらんだりして余計なことをしていたこともあったんですが…。pltを使って路線図を地図上に出力できそうなので試していました。無駄に時間かかった。
結果成功しましたが分析に役立つことはなかったです。これも目的意識がしっかりしていれば防げていたかもしれません。
データの動きは細かく確認するといい
機械学習に限らず、コードを書く時のエラーの半分くらいがスペルミスとかの記述ミスで、半分くらいがデータの動きが想定外の挙動をしているミスな気がしています。
昔プログラミングをかじったときにエラーしまくった経験から、私はいちいちprintしてデータの確認をしています。
そのおかげで変な挙動でのミスはそんなになかったような気がします。それでもいくらかあったんですが…。
それよりも記述ミスが頻出して憤慨していたような気がします。つら。
コードは見やすさと使いまわしやすさと書き換えやすさを気にするといい
今回のSUUMO分析の中でかなり実感しました。
SUUMO分析の前にも別の分析をしていたのですが、ファイル一つの中に次々とコードを書きこんでいくので、どこに何があるのかわからなくなっていました。
その反省から、まず同じ分析でもやることが違うならファイルを分けました。
ファイルの中でもやることごとに見出しをつけておいて何をしているのかわかりやすくしました。なるべくコメントも付けるようにしました。
そのおかげで前にやったことを確認しやすくなりましたし、使いまわしもしやすくなりました。
今回の分析のおかげで使いまわしに関してはかなり考え方が身についたような気がします。
いまだに後で見返して意味わからんコメントがたまにありますが
知識量はそのまま視点の多さになるのでドメイン知識を調べたほうがいい
これは特に機械学習はそうだと思います。そうでなくても、引き出しの多さは武器になると思うのでいいと思いますが。
私は特に興味がない分野に対して疎いほうだと思っています。他の人との話題のためにいろいろ調べとくとかあんまりしないですし…。
ので、今回の物件情報とか立地情報についても大して知らない状態から始まりました。
物件情報について多少下調べをしましたが、下調べするにしても知識があるとないで調べる手がかりに差が出るんですよね。
下調べするだけでも知識を持っているかどうかで調べられる範囲が全然違うと思います。
それで大した下調べも出来ず、記述統計でいろいろ確認していく中でなんで?って思うことがたくさんあったのでその都度調べていました。
あと隣にいた知り合いを見ていて思ったことですが、引き出しが多いことで視点が増えるんですよね。
私は立地も地名も大して知らなかったので港区とか千代田区とか聞いてもフーンって気分だったんですけど、知り合いは「あー立地ええとこやんなー」ってすぐ気が付いていたので、知り合いが分析を進めていたらもっと捗っているような気がします。
とにかく、知識の多さは引き出しの多さと視点の多さにつながるので、いろんなことを勉強しておくといいと思いました。
私はこれまで興味の対象が狭かったので今後が心配です…
学び優先ならテキストやガイドそのまんまより自分のやりたいことの中で活用できるといい。初めてやることや時間優先ならコピペをうまく使った方がいい
私は昔に塾の先生をしていた時があるのでその時の考えが濃く残っているような気がしますが、
テキストやガイドをそのまま書き写す勉強をするのは極めて危険だと思っています。
今回のSUUMOアプリ開発の中で、その考えがその通りだと思った時とそうでもないなと思った時がありました。
何も知らないところから始めることはできないので、私がアプリを作り始める前にQiita記事にあったアプリ開発実例を教材にして、「ちょっと書き換えて遊びながら」学びました。
そこからSUUMOのアプリを作り始めたのですが、「ちょっと書き換えて遊びながら」学んだことでかなり理解が深まったと思っています。
というのも、自分で書き換えられるということはつまりコードの意味を理解していることになるので、「こうすればこうなるんじゃね?」と思いながら書き換えるといい勉強になります。
エラーが出るなら出るで想定外だったことがわかるので、そこから正しい仕様を理解できます。
コピペだとこうはいかなくて、細かいコードとか気にしてないので何がどうなってうまく行っているのか理解するのは難しいと思います。
で、テキストどおりが危険という考えが案外そうでもないかもしれないと思ったのが、アプリにデータベースを利用する時に仕様がよくわからずに迷走することがあった時です。
web上の実装例とかを見ながら、自分のアプリならこうすればいけそうと思いながらコードを書き進めていたのですが、こうすればいけそうが悉くいけなかったのでかなり迷走しました。
結局いったん実装例をそのまま書いてそれを改変することでやりたかったことを実現することになりました。
今思うとかなり遠回りをしたような気がします。
今の自分の考えとしては、自分である程度思い通りにコードを書けるようにしたいならテキストやガイドを自分で書き換えながら学ぶのが良くて、
初めてやる状態ならいったん書き写してどうなっているのか確認したほうがいいという感じで落ち着きました。
若しくは、急ぎだったりエラー防止のためにコピペを活用する分にはいいと思いますけどね。学びを度外視すればコピペはかなり効率が良いです。
Qiita記事を書くことにもメリットデメリットがあった
一応語っておきたいこととして、Qiita記事を書くことにメリットもデメリットもあったことを述べます。
見世物にするのでちゃんと形になる
Qiita記事に書くってことは誰か見る人がいるってことじゃないですか。対して見られもしないクソ記事でも数百人くらいは目を通します。
ので、見世物にするからにはちゃんと形にしようって考えが働きました。それもあってコードも後で見返しやすいように書いていた節があります。
おかげでコードは見やすくなったし自分の成果物もちゃんと形になったし(あんまりきれいじゃないけど)まとまってくれたので、その意味で良かったと思います。
データサイエンティストって研究を報告だか発表だかすることになると思うので、その練習としてよかったんじゃないでしょうか。少なくとも偉い人に情報共有はするでしょうからね。
時間がかかる
当然ですけど記事書くのに結構な時間がかかるので、その分勉強時間とか他の分析の時間が失われているって考えると、もしかしたら他の人より歩くの遅いんじゃないかな~って思うことはあります。
その分Qiita記事を書いて思考が整理されたりまとまったりするので、深い学びが出来ていると思いますが。↑のメリットと合わせて一長一短です。
SUUMO分析終わり。
SUUMO分析の活動を始めてからそこそこ時間がかかりましたが、やれそうなことはやれたのでここで終わりにします。
途中で目がくらんだりで放棄せず最後までやり通すことができてよかったです。
とはいっても目がくらんで他のことに手を付ければその分幅広い勉強ができていたんですよね。
その点はちょっと羨ましいなーと思わなくもないです。
でも時間をかけた分深い勉強ができたので、私は決して悪くないと思っています。(世間がどう思うかはさておき)
今後は分析の幅を広げたいので、時系列データの分析、たぶん天気予報をすると思います。
データの集めやすさと身近で分かりやすいこととアプリにするイメージがわきやすいので天気予報です。
無論自分は天気予報なんて高校までに習う範囲以上のことは知らないので、やっぱりいろいろ調べまわりながら分析することになると思います。
「天気予報なんて全然知らんワイが機械学習の力を借りてお天気お兄さんになってみた」とかそんな感じです。
それともう一つ、Qiita記事のメリットデメリットの話を書きましたが、Qiita記事書いててよかったなーって思います。
さっきのメリットの話もありますし、おかげさまで300以上いいねされた記事もありました。ご覧いただいた多くの皆様本当にありがとうございます。
見てもらえる記事を書くことも一つのモチベになっていたような気がします。
Qiitaって仕様をまとめてある記事とか学んだことのまとめ記事が多い印象ですが、個人的にはみんなが読みたくなるような愉快な記事が増えてくれたらうれしいです。機械学習に興味を持ってくれる人が増えて欲しいですからね。
(Qiita本来の目的から外れるかもしれませんが)
まとまりませんが、これで終わりにします。
最後までご覧いただいた皆様、ありがとうございました。