Rubyの線形回帰で未来を予測する方法
私たちが日々行う選択の多くは、実は数値的な関係に基づいています。
- 科学者がコレステロールを下げると言うから、特定の食品を食べる
- 収入が増えやすいから、学歴を積む
- 最も資産価値が上がりそうだと考えた地域に、家を買う
これらの結論には、どうやって到達するのでしょうか?おそらく、誰かが大量のデータを収集し、それを分析して結論を導き出したのでしょう。よく使われる手法のひとつが「線形回帰」で、これは教師あり学習の一種です。教師あり学習の詳細や活用例については、このシリーズの第1回をご覧ください。
線形関係とは
2つの値(xとyとします)の間に「線形関係」があるとは、xが1変化すると、yも必ず一定の量だけ変化することを意味します。例を挙げた方が分かりやすいでしょう。
- ピザ10枚の値段は、ピザ1枚の10倍になる
- 高さ10フィートの壁を塗るには、5フィートの壁の2倍のペンキが必要になる
数学的には、このような関係は直線の方程式で表されます。
y = mx + b数学はとっつきにくいものですが、まるで魔法のように感じられることもあります。私が初めて直線の方程式を学んだとき、たった1つの式で距離や傾き、直線上のさまざまな点を計算できることに、美しさすら感じたのを覚えています。
しかし、手元にあるのがデータポイントだけの場合、この式はどうやって求めればいいのでしょうか?その答えが線形回帰です。非常に人気のある機械学習の手法です。
線形回帰の実例
この記事では、楽曲のBPM(1分あたりのビート数)が、Spotifyでの人気度を予測できるかどうかを検証します。
線形回帰は2つの変数の関係をモデル化します。一方を「説明変数」、もう一方を「目的変数(従属変数)」と呼びます。
今回の例では、BPMが人気度を「説明」できるかどうかを調べたいので、BPMが説明変数、人気度が目的変数となります。
モデルは最小二乗法を用いて、ご想像の通り y = mx + b の形式に最もよく当てはまる直線を見つけ出します。
説明変数は複数あっても構いませんが、この例では説明変数が1つだけの「単回帰分析」を行います。
最小二乗法とは?
線形回帰にはいくつかの手法がありますが、そのひとつが「最小二乗法」です。これは、各データポイントから直線までの垂直偏差の二乗和を最小化することで、最適な当てはめ直線を計算します。
難しく聞こえるかもしれませんが、要するに「データポイントと直線の間の距離が最小になるような直線を作ってください」と言っているだけです。
二乗して合計するのは、正の値と負の値が互いに打ち消し合わないようにするためです。

Quoraで見つけた上記の画像が、この仕組みをとても分かりやすく説明してくれています。
データセットの準備
今回はKaggleのこちらのデータセットを使用します:https://www.kaggle.com/leonardopena/top50spotify2019 CSV形式でダウンロードできます。
このデータセットには16の列がありますが、必要なのは「Track Name(曲名)」「Beats Per Minute(BPM)」「Popularity(人気度)」の3つだけです。機械学習における最も重要なステップのひとつは、データを適切な形式に整えることで、これは「データマンジング(munging)」と呼ばれることもあります。上記の3列以外のデータはすべて削除してしまいましょう。
CSVは次のようになります。
Rubyで回帰分析を行う
この例では、ruby_linear_regression gemを使用します。インストールするには、次のコマンドを実行します。
gem install ruby_linear_regressionそれでは、コーディングを始めましょう!新しいRubyファイルを作成し、次のrequire文を追加します。
require "ruby_linear_regression"
require "csv"次に、CSVデータを読み込み、#shiftを呼び出してヘッダー行を破棄します。あらかじめCSVファイルから最初の行を削除しておいても構いません。
csv = CSV.read("top50.csv")
csv.shiftxデータポイントとyデータポイントを格納するための、空の配列を2つ作成します。
x_data = []
y_data = []そして.eachメソッドで繰り返し処理を行い、Beats Per Minuteのデータをx配列へ、Popularityのデータをy配列へ追加していきます。
実際に何が起きているのか気になる方は、
putsやpでrowを出力してみてください。例:puts row
csv.each do |row|
x_data.push( [row[1].to_i] )
y_data.push( row[2].to_i )
endいよいよruby_linear_regression gemを使います。回帰モデルの新しいインスタンスを作成し、データを読み込んで、モデルを訓練します。
linear_regression = RubyLinearRegression.new
linear_regression.load_training_data(x_data, y_data)
linear_regression.train_normal_equation次に、平均二乗誤差(MSE)を出力します。これは観測値と予測値の差を表す指標です。差は二乗されるため、正負の値が打ち消し合うことはありません。予測値と実際の値の距離が大きくならないように、MSEは最小化したい値です。
puts "Trained model with the following cost fit #{linear_regression.compute_cost}"最後に、モデルを使って予測を行ってみましょう。具体的には、BPM 250の楽曲はどれくらい人気になるのでしょうか?prediction_data配列の値をいろいろ変えて試してみてください。
prediction_data = [250]
predicted_popularity = linear_regression.predict(prediction_data)
puts "Predicted popularity: #{predicted_popularity.round}"実行結果
プログラムをコンソールで実行して、結果を確認してみましょう!
➜ ~ ruby spotify_regression.rb
Trained model with the following cost fit 9.504882197447587
Predicted popularity: 91いいですね!次に「250」を「50」に変えて、モデルが何を予測するか見てみましょう。
➜ ~ ruby spotify_regression.rb
Trained model with the following cost fit 9.504882197447587
Predicted popularity: 86どうやら、BPMが高い楽曲ほど人気が高い傾向があるようです。
プログラム全体
参考までに、私のファイル全体は次のようになります。
require 'csv'
require 'ruby_linear_regression'
x_data = []
y_data = []
csv = CSV.read("top50.csv")
csv.shift
# CSVファイルからデータを読み込み、2つの配列に格納します
# 1つは独立変数X用(x_data)、もう1つは従属変数y用(y_data)です
# Row[0] = 曲名
# Row[1] = BPM
# Row[2] = 人気度
csv.each do |row|
x_data.push( [row[1].to_i] )
y_data.push( row[2].to_i )
end
# 回帰モデルを作成
linear_regression = RubyLinearRegression.new
# 訓練データを読み込む
linear_regression.load_training_data(x_data, y_data)
# 正規方程式を使ってモデルを訓練
linear_regression.train_normal_equation
# コストを出力
puts "Trained model with the following cost fit #{linear_regression.compute_cost}"
# BPM 250の楽曲の人気度を予測
prediction_data = [250]
predicted_popularity = linear_regression.predict(prediction_data)
puts "Predicted popularity: #{predicted_popularity.round}"次のステップ
これは非常にシンプルな例ですが、それでもあなたは機械学習の重要な手法である線形回帰を、初めて実行したことになります。さらに学びたい方は、次のようなことに挑戦してみてください。
- 使用したRuby gemのソースコードを調べて、内部で行われている数学を確認する
- 元のデータセットに戻り、モデルに追加の変数を加えて重回帰分析を実行し、MSEを減らせるか試してみる。例えば「valence(楽曲のポジティブさ)」も人気度に影響しているかもしれません
ruby_linear_regressiongemで実行できる勾配降下法モデルを試してみる
-
RubyでNクイーン問題を解く方法【バックトラッキングの実装を徹底解説】
N-Queens(Nクイーン)は、N×Nの盤面にN個のクイーンを配置する、非常に興味深いコーディングチャレンジです。 イメージは以下のようになります。 クイーンは全方向に移動できる駒です。 縦方向 横方向 斜め方向 この問題の解答(複数存在する場合もあります)では、すべてのクイーンを盤上に配置しつつ、どのクイーンも他のクイーンの攻撃範囲に入らないようにする必要があります。 この記事では、私がどのような思考プロセスで解答にたどり着いたのかを紹介します。 まずは計画を立てる この種のチャレンジに取り組むときは、まず平易な言葉で計画を書き出すことから始めるのが効果的です。 そうすることで、「何
-
Rubyのtransposeメソッドで行を列に変換する方法
Rubyでグリッド状のデータ(多次元配列)を扱うときに便利なのが、Arrayクラスのtransposeメソッドです。この記事では、行と列を入れ替える「転置」の基本から、三目並べ(○×ゲーム)のような実践的な活用例までをわかりやすく解説します。 たとえば、3×3の正方形グリッドを多次元配列として持っているとしましょう。ここから「行を列に変換したい」という場面は意外とよくあります。 なぜそんなことが必要になるのでしょうか? 代表例が、古典的なゲームである三目並べです。 盤面をグリッドとして保存し、勝利判定を行うには、行・列・斜めのすべてをチェックしなければなりません。 ところが、グリッドを普通の配