本文へ移動

ホーム / ML / 第3部 データと特徴量

第3部 データと特徴量・第5章

データと特徴量

★★★ 必須10トピック・面接 5問

モデルの性能の大部分を決めるデータと特徴量の扱いを押さえる。

別のページで開く →

DBラボを読み込んでいます…

5.1ラベル付けと品質管理

用語★★☆ 重要
ひとことで

質の良いラベルが何より大事。ラベルの揺れやバイアスがそのままモデルに入る。

教師あり学習のモデルは、ラベル(正解)以上には賢くなれません。ラベルが揺れていれば、モデルも揺れます。データを集めるときは、量より先にラベルの質を確保します。

一致しないところは、指示書が曖昧なところ指示書基準と境界の例重ねて付ける1割を2〜3人で一致率を測るカッパ係数など不一致を見る基準を足す・直す正解が分かっている問題を混ぜて、作業者ごとの品質も確かめる
ラベルの品質を上げるループ

ラベル付けの指示と品質管理:

  • 指示書(ガイドライン)を作る:判断の基準と、迷う例(境界の例)を具体的に書きます。「スパムとは何か」を人によって違うまま付けると、モデルは何を学べばよいか分かりません。
  • 同じデータを複数人で付ける:一部(例:1割)を2〜3人で重ねて付け、一致率(カッパ係数など)を測ります。一致しないところは、指示書が曖昧な場所です。
  • 正解が分かっている問題を混ぜる:作業者の品質を確かめ、低い人の分を見直します。
  • バイアスに注意:作業者の思い込みや、特定の種類のデータだけ集めたことによる偏りは、そのままモデルに入ります。
  • ラベルの代わりに行動を使う:クリック・購入・解約などの記録をラベルにできれば、人手が要りません。ただし「表示されなかったものはクリックされない」など、集め方による偏りがあります。

面接ではこう答える

教師あり学習のモデルはラベル以上には賢くなれないので、量より先にラベルの質を確保します。判断の基準と迷う例を書いた指示書を作り、一部のデータを複数人で重ねて付けて一致率を測ります。一致しないところは指示書が曖昧な部分なので、基準を足して直します。正解が分かっている問題を混ぜて作業者の品質も確かめ、作業者の思い込みや集め方による偏りがないかにも注意します。

ひとことで

モデルが一番迷っているデータを選んで人にラベルを付けてもらい、少ない作業で効果を上げる方法。

能動学習:モデルが迷うデータを優先して、人にラベルを付けてもらう学習今あるラベルで予測ラベルのないデータ迷うものを選ぶ確率が 0.5 付近など人がラベル付け指示書に沿ってラベルを足して再学習する。同じ作業量で効果が上がりやすい
能動学習のループ

能動学習(active learning):全部にラベルを付けるのではなく、モデルが一番迷っているデータ(確率が 0.5 付近など)を選んで人に付けてもらう方法です。すでに自信を持って当てられるデータにラベルを足しても学びは少ないので、同じ作業量でより効果が上がります。「学習 → 迷うデータを選ぶ → ラベル付け → 再学習」をくり返します。最近は、LLM に下書きのラベルを付けさせて人が確認・修正する方法もよく使われます。

方法向いている場面注意
能動学習ラベル付けの予算が限られている迷うデータばかり集めると、分布が偏る。一部はランダムにも選ぶ
LLM の下書き+人の確認文章の分類・抽出など、指示で判断できるタスクLLM の誤りの傾向が入る。人の確認の割合と一致率を記録する
行動のログをラベルにするクリック・購入・解約など表示されたものしか選ばれない、などの偏りがある

面接ではこう答える

能動学習は、全部のデータにラベルを付けるのではなく、モデルが一番迷っているデータ、たとえば確率が 0.5 付近のものを選んで人に付けてもらう方法です。自信を持って当てられるデータにラベルを足しても学びは少ないので、同じ作業量で効果が上がります。学習、迷うデータの選択、ラベル付け、再学習をくり返します。最近は LLM に下書きのラベルを付けさせて人が確認する方法も使われますが、一致率を測って品質を確かめます。

ひとことで

欠損値・スケール・外れ値などを整える作業。変換に使う値は学習データだけで求め、モデルと一緒に保存する。

前処理は、生のデータをモデルが扱える形に整える作業です。何をするかはモデルによって違い、勾配ブースティングならほとんど要らず、線形モデルやニューラルネットでは結果を大きく左右します。

平均・標準偏差などは学習データだけで求め、同じ値で他を変換する学習データfit:平均などを求める前処理の設定保存して使い回す検証・テストtransform だけ本番の推論同じ設定で変換全データで平均を出してから分けると、テストの情報が学習に入る(リーク)
前処理は学習データだけで「学ぶ」
処理やり方の例注意
欠損値中央値で埋める・「欠損している」フラグの列を足す・そのまま渡す(GBDT)欠損していること自体に意味があることが多い(未入力・未購入)
スケーリング標準化(平均0・分散1)・最小値と最大値で 0〜1 に線形モデル・距離を使う手法・ニューラルネットで必要
外れ値上限・下限で切る・対数をとる入力ミスか本当の値かを確かめてから。不正検知では外れ値が答えのこともある
偏った分布金額・回数は対数をとる0 があるときは log(1+x)
日時曜日・時間帯・月末・祝日・前回からの経過日数タイムゾーンをそろえる

前処理は、scikit-learn の Pipeline のようにモデルと1つにまとめて保存し、学習と推論で同じものを使います。別々に書くと、学習と推論でずれる原因になります。

面接ではこう答える

前処理は、欠損値を埋める、値のスケールをそろえる、外れ値を切る、偏った分布を対数で整える、などの作業です。必要さはモデルによって違い、勾配ブースティングではほとんど要りませんが、線形モデルやニューラルネットでは大きく効きます。大事なのは、平均などの変換に使う値を学習データだけで求めることと、前処理をモデルと1つのパイプラインにまとめて学習と推論で同じものを使うことです。

ひとことで

種類が多いカテゴリの列は、ワンホットでなく、頻度・ターゲットエンコーディング・ハッシュ・埋め込みなどで数にする。

都道府県・商品 ID・端末の種類のようなカテゴリの列は、そのままでは数として扱えないので変換します。種類の数(カーディナリティ)によって向く方法が違います。

方法やり方向いている場面・注意
ワンホット種類ごとに 0/1 の列を作る種類が少ない(数十まで)。多いと列が爆発する
頻度・件数その種類が何回出てくるかに置き換える簡単で漏れがない。人気の度合いが効く問題で使える
ターゲットエンコーディングその種類での正解の平均(購入率など)に置き換える種類が多くても強い。自分の行の正解が混ざるとリークになるので、交差検証の別の分割(out-of-fold)で計算し、件数の少ない種類は全体の平均に寄せる
ハッシュ種類をハッシュで決まった数の列に振り分ける新しい種類が次々出てくる(URL など)。衝突は少し起きる
埋め込み種類ごとのベクトルを学習する(ニューラルネット)商品 ID・ユーザー ID のように種類がとても多い。推薦でよく使う
GBDT のカテゴリ機能LightGBM・CatBoost にカテゴリとして渡す手軽だが、種類が多いと過学習することがある

学習データに無かった新しい種類(新商品)は、推論で必ず出てきます。「その他」に寄せる、平均の値を使う、など扱いを決めておき、テストでも新しい種類の割合を確かめます。

面接ではこう答える

カテゴリの列は、種類が少なければワンホットで十分ですが、商品 ID のように種類が多いと列が爆発します。その場合は、出現の頻度、その種類での正解の平均に置き換えるターゲットエンコーディング、ハッシュで決まった数の列に振り分ける方法、ニューラルネットで埋め込みを学習する方法を使います。ターゲットエンコーディングは自分の行の正解が混ざるとリークになるので、交差検証の別の分割で計算します。新しい種類への対応も決めておきます。

5.5特徴量ストア

用語★★☆ 重要
ひとことで

学習(オフライン)と推論(オンライン)で同じ定義の特徴量を使うための仕組み。ずれると精度が落ちる。

モデルは、学習のときと推論のときで、同じ意味の入力をもらう前提で作られています。ところが、学習用の特徴量はデータサイエンティストが SQL で、推論用はアプリのエンジニアが別の言語で、と別々に作ると、集計の期間や欠損の扱いが少しずつずれます。すると、オフラインの評価はよいのに本番では精度が出ない、ということが起きます(training-serving skew)。

特徴量ストアは、このずれを防ぎ、作った特徴量をチームで使い回すための仕組みです。

特徴量ストアは、特徴量(モデルの入力にする値。例:「直近7日の購入回数」)を、1つの定義から作り、学習と推論の両方に配る仕組みです。学習用には過去の大量のデータ(オフラインのストア。データウェアハウスなど)、推論用には最新の値をすぐ返す保存先(オンラインのストア。Redis などの KVS)を持ちます。製品としては Feast などのオープンソースや、各クラウドのサービスがあります。

オフラインのストアオンラインのストア
使う場面学習データを作る推論のときに取り出す
持つデータ過去の全履歴(いつの値かの時刻つき)各キーの最新の値
保存先の例データウェアハウス・Parquet のファイルRedis などの KVS
求められること大量に読める・時点を指定して結合できる数 ms で返る
1つの定義から、学習用と推論用の両方に同じ特徴量を配る特徴量の定義1か所にまとめるオフラインのストア過去の全履歴(DWH)学習point-in-time で結合オンラインのストア最新の値(KVS)推論数 ms で取り出す別々に作り直すと「7日に今日を含むか」などがずれて、精度だけが静かに落ちる
特徴量ストアの構成

小さなチームなら、最初から製品を入れる必要はありません。特徴量を作る SQL や関数を1か所にまとめ、学習と推論で共有するだけでも、多くのずれを防げます。

下の「ブラウザで試す」の図は推論 API の流れで、特徴量ストアの役割は「特徴量を取得」の手順にあたります。アプリからの依頼を受けた推論サーバーが、オンラインのストア(Redis など)から、学習時と同じ定義の特徴量を取ってから計算します。オフラインのストアや学習データを作る側は図には出てこないので、上の表と構成図で補ってください。

面接ではこう答える

特徴量ストアは、特徴量を1つの定義から作り、学習と推論の両方に配る仕組みです。学習用には時刻つきの過去の全履歴を持つオフラインのストア、推論用には各キーの最新の値を数ミリ秒で返すオンラインのストアを持ちます。一番の役割は、学習と推論で特徴量の作り方がずれるのを防ぐことと、過去のある時点の値を正しく結合する point-in-time の結合です。小さなチームなら、製品を入れずに特徴量の定義を1か所にまとめるだけでも効果があります。

別のページで開く →

ネットワーク図を読み込んでいます…

ひとことで

学習のときと推論のときで、特徴量の作り方や値がずれること。エラーは出ずに精度だけが落ちる。

同じ「直近7日の購入回数」でも、作り方が2つあるとずれる学習:Python と SQL今日を含まない・欠損は 0・UTC推論:アプリのコード今日を含む・欠損は平均・日本時間対策:定義を1か所に(特徴量ストア・共通の関数)/推論で使った特徴量をログに残して学習に使うエラーは出ないのに、精度だけが落ちる
training-serving skew:作り方が2つあると、ずれる

training-serving skew(学習と推論のずれ):学習のときは Python と SQL で、推論のときはアプリのコードで特徴量を作り直すと、細かいところがずれます。たとえば「7日」に今日を含むか、欠損値を 0 にするか平均にするか、タイムゾーンが違う、などです。モデルは学習したときと違う入力を受け取り、エラーは出ないのに精度だけが落ちます。対策は次のとおりです。

  • 特徴量の定義を1か所にまとめ、学習と推論で同じコード・同じ定義を使う(特徴量ストアの一番の役割)。
  • 推論の時に使った特徴量をログに残し、それを次の学習に使う(ずれようがない)。
  • 本番の特徴量の分布と、学習データの分布を定期的に比べる。

面接ではこう答える

training-serving skew は、学習と推論で特徴量の作り方がずれる問題です。たとえば学習は SQL、推論はアプリのコードで「直近7日」を作り、今日を含むか、欠損を何で埋めるか、タイムゾーンが違う、といったずれが起きます。モデルは学習したときと違う入力を受け取り、エラーは出ないまま精度だけが落ちます。防ぐには、特徴量の定義を1か所にまとめる、推論で使った特徴量をログに残して次の学習に使う、本番と学習の分布を定期的に比べる、を行います。

ひとことで

学習データの各行に、その行の予測時点で分かっていた値だけを結合すること。未来の値の混入を防ぐ。

各行には「予測した時刻より前で、最新の値」だけを付ける値=3値=5値=9予測した時刻(3月1日)使う:値=5使わない:値=9(未来)特徴量の値には「いつの値か」の時刻を必ず持たせる
point-in-time の結合:その時点で分かっていた値だけを付ける

時点を正しく取る(point-in-time):学習データを作るとき、各行の「予測した時点」で分かっていた値だけを結合する必要があります。3月1日の行に、今日時点の最新の「累計購入回数」を付けると未来の情報が混ざります(リーク)。特徴量ストアは、値ごとにいつの値かの時刻を持ち、「この時刻以前で最新の値」を結合する機能(point-in-time join)を持っています。自分で SQL を書く場合も同じ考え方で、時刻の条件を必ず付けます。

-- 各予測の行に、その時刻より前で最新の特徴量の値を付ける(考え方)
SELECT p.user_id, p.predicted_at, f.value
FROM predictions p
JOIN features f
  ON f.user_id = p.user_id
 AND f.updated_at = (SELECT MAX(f2.updated_at) FROM features f2
                     WHERE f2.user_id = p.user_id
                       AND f2.updated_at <= p.predicted_at);

面接ではこう答える

point-in-time の結合は、学習データを作るときに、各行の予測した時点より前で最新の特徴量の値だけを結合する方法です。3月1日の行に今日時点の累計購入回数を付けると、未来の情報が混ざるリークになります。そのため特徴量の値には必ず「いつの値か」の時刻を持たせ、時刻の条件付きで結合します。特徴量ストアはこの機能を持っていますが、自分で SQL を書く場合も同じ考え方です。

ひとことで

集計・ウィンドウ関数で特徴量を作る。基準日より前のデータだけを使う。

表形式のデータの特徴量の多くは、SQL の集計で作れます。データは DB やデータウェアハウスにあるので、そこで集計してから取り出すほうが速く、定義も共有しやすくなります。大事なのは、基準日(予測する時点)より前のデータだけを使うことです。下の DB ラボで、同じ SQL を MariaDB・PostgreSQL・SQLite で試せます。

行をまとめずに、「同じユーザーのこれまでの注文の回数」「直前 3 回の平均」のような値を各行に付けられます。

この行より前の行だけを集計する(今の行と未来は含めない)今の行(予測したい注文)ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING集計に使う(過去)未来:使わない注文の時刻ORDER BY を書かずに PARTITION BY だけにすると、全期間(未来も)を集計してリークになる
ウィンドウ関数のフレームとリーク
SELECT id, ordered_at, total,
  -- この注文より前の注文の回数
  COUNT(*) OVER (PARTITION BY user_id ORDER BY ordered_at
    ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) AS past_orders,
  -- 直前 3 回の平均金額
  AVG(total) OVER (PARTITION BY user_id ORDER BY ordered_at
    ROWS BETWEEN 3 PRECEDING AND 1 PRECEDING) AS avg_last3
FROM orders
WHERE user_id = 7
ORDER BY ordered_at;

集計する範囲(フレーム)を … AND 1 PRECEDING で終わらせ、「今の行を含めない」ようにしているのがポイントです。

フレームの指定含む行使ってよい?
ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDINGそれより前の行だけよい
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW今の行を含む今の行の値(答え)が混ざることがある
PARTITION BY user_id だけ(ORDER BY なし)全期間(未来も)リークになる

面接ではこう答える

表形式のデータの特徴量の多くは、DB やデータウェアハウスで SQL の集計として作れます。ウィンドウ関数を使うと、行をまとめずに「そのユーザーのこれまでの注文回数」や「直前3回の平均」を各行に付けられます。このとき集計の範囲を「1つ前の行まで」にして、今の行や未来の行を含めないことが大事です。基準日より前のデータだけを使えば、リークを防げます。

別のページで開く →

DBラボを読み込んでいます…

基準日より前は特徴量に、後はラベルに使う特徴量:6月1日より前の注文ラベル:6月中に買ったか基準日 6月1日基準日を何日分もずらして作ると、学習データを増やせる(同じ人が何度も出るので分け方に注意)
基準日で切って、特徴量とラベルを作る

「6月1日の時点で、翌月に買うか」を予測するなら、特徴量は 6月1日より前の注文だけで作り、ラベルは 6月の注文から作ります。結合の条件に日付を入れるのが、リークを防ぐ基本の形です。

SELECT u.id AS user_id,
  COUNT(o.id) AS orders_before,
  COALESCE(SUM(o.total), 0) AS spent_before,
  CASE WHEN EXISTS (SELECT 1 FROM orders f
         WHERE f.user_id = u.id
           AND f.ordered_at >= '2025-06-01' AND f.ordered_at < '2025-07-01')
       THEN 1 ELSE 0 END AS bought_next_month
FROM users u
LEFT JOIN orders o
  ON o.user_id = u.id AND o.ordered_at < '2025-06-01'
GROUP BY u.id;

この SQL は、SQL での特徴量づくりの DB ラボ(MariaDB・PostgreSQL・SQLite)で試せます。

ID から計算した値で分けると、再実行しても同じ人は同じ側user_id例:1234混ぜて 10 で割った余り0〜9 のどれか0〜7 → 学習(8割)8〜9 → テスト(2割)ランダムな分け方と違い、データが増えても既存の人の側は変わらない
ID のハッシュで分けると、何度実行しても同じ分け方になる

ランダムに分けると、実行するたびに分け方が変わって再現できません。ユーザー ID から計算した値で分けると、何度実行しても、データが増えても、同じユーザーは同じ側に入ります。下の例は、ID に大きな数を掛けて混ぜてから 10 で割った余りを使う、どの DB でも動く簡単な方法です(MD5 などのハッシュ関数を使う方法もありますが、関数の名前が DB によって違います)。

SELECT CASE WHEN (user_id * 2654435761) % 10 < 8 THEN 'train' ELSE 'test' END AS split,
  COUNT(DISTINCT user_id) AS users, COUNT(*) AS rows_
FROM orders
GROUP BY 1;

ユーザー単位で分けるので、同じユーザーが学習と評価の両方に入るリークも防げます。時間で変わる問題では、これに加えて時間でも分けます。

この SQL は、SQL での特徴量づくりの DB ラボ(MariaDB・PostgreSQL・SQLite)で試せます。

この章のまとめ

🖨 まとめシート(印刷・PDF)
  • 学習データの集め方・ラベル付け・前処理
  • 特徴量ストアで学習と推論の特徴量をそろえる

この章のクイズ

面接ではこう聞かれる

まず答えを見ずに声に出して答え、そのあと開いて比べる。

Q1学習時と推論時で特徴量がずれる問題をどう防ぎますか?

模範回答

一番の対策は、特徴量の定義を1か所にまとめて、学習と推論で同じコードを使うことです。学習は SQL、推論はアプリで別々に書くと、期間の数え方や欠損値の扱い、タイムゾーンなどが少しずつずれます。規模が大きければ特徴量ストアを使い、オフラインとオンラインの両方に同じ定義から値を配ります。学習データを作るときは、各行の時点で分かっていた値だけを結合する point-in-time の結合にします。

そのうえで、推論で実際に使った特徴量をログに残し、学習データの分布と定期的に比べて、ずれたら気づけるように監視します。可能なら、そのログ自体を次の学習データに使います。

深掘りされたら

  • リアルタイムの特徴量(直近5分のクリック数など)はどう作りますか?

見られているポイント定義の共通化・特徴量ストア・監視

Q2point-in-time の結合とは何ですか?なぜ必要ですか?

模範回答

学習データの各行に、その行の時点で分かっていた特徴量の値だけを結合することです。たとえば 3月1日の予測の行には、3月1日より前に記録された値のうち一番新しいものを付けます。

必要な理由はリークを防ぐためです。今日時点の最新の値(たとえば累計の購入回数)を過去の行に付けると、未来の情報が入り、オフラインの評価だけ良くなって本番では外れます。特徴量ストアにはこの結合の機能があり、SQL で書くなら結合の条件に「その時刻以前」を必ず入れます。

深掘りされたら

  • 後から修正された値は、どう扱いますか?

見られているポイント時点ごとの値・リークとの関係・実装の方法

Q3ラベルの品質をどう確かめますか?

模範回答

一部のデータを複数人で重ねてラベル付けして、一致率を測ります。一致しないところは、指示書の基準が曖昧なところなので、迷う例を指示書に足します。答えの分かっている問題を混ぜて作業者ごとの品質を見る、専門家が抜き取りで見直す、といった方法も組み合わせます。

学習したモデルを使う方法もあります。モデルが自信を持って外しているデータは、ラベルが間違っていることが多いので、優先して見直します。

深掘りされたら

  • ラベルの付け方を途中で変えたら、何をしますか?

見られているポイント一致率・指示書の改善・モデルを使った誤りの発見

Q4カテゴリの種類がとても多い列(商品 ID など)は、どう特徴量にしますか?

模範回答

種類が多い列を one-hot(種類ごとに 0/1 の列を作る)にすると、列が多すぎて扱いにくくなります。よく使うのは、ターゲットエンコーディング(そのカテゴリでの正解の平均を値にする)、出現回数で置き換える、埋め込み(ニューラルネットで各カテゴリをベクトルにする)です。勾配ブースティングの中には、カテゴリをそのまま扱えるものもあります。

ターゲットエンコーディングは、自分自身の正解を含めて平均を取るとリークになるので、交差検証の分割ごとに、他の分割のデータだけで計算します。件数の少ないカテゴリは値が不安定なので、全体の平均に寄せるか、「その他」にまとめます。

深掘りされたら

  • 学習時になかった新しいカテゴリが本番で来たら、どうしますか?

見られているポイント手法の選択肢・ターゲットエンコーディングのリーク

Q5データが少ないときは、どうしますか?

模範回答

まず、モデルを工夫する前にデータを増やせないかを考えます。ラベル付けを頼む場合は、能動学習でモデルが迷うデータから優先して付けると、少ない作業で効果が上がりやすいです。画像ならデータ拡張、文章なら LLM で下書きのラベルを作って人が直す方法もあります。

モデルの側では、事前学習済みのモデルを使う転移学習や、単純なモデル・強めの正則化を選びます。評価は交差検証にして、成績のばらつきも見ます。少ないデータで複雑なモデルの成績が良いときは、過学習やリークを疑います。

深掘りされたら

  • LLM に作らせたラベルを学習に使うときの注意は?

見られているポイントデータを増やす工夫・転移学習・評価の安定