AI: データの基礎
最終更新:2026-08-26
データは AI の燃料です―データがなければ、どんなに優れたアルゴリズムでも有効なモデルを学習できません。この章では、データ型、特徴エンジニアリング、データセット分割、そして「Garbage In, Garbage Out(粗悪な入力は粗悪な出力を生む)」という鉄則を理解します。
1. 学習目標
- AI におけるデータの中心的役割
- 構造化データと非構造化データ
- 特徴とラベル
- データセット分割:学習 / 検証 / テスト
- データ品質はどう AI 性能に影響するか
2. 機械学習プロジェクトの実話
(1) 悩み:1 ヶ月学習して精度が 60%
Bob のチームは 1 ヶ月かけて画像分類モデルを学習しましたが、精度はわずか 60% でした。調査すると、学習データの 70% が晴天の画像で占められており、実世界のシナリオには雨や霧、夜間など多様な天候が含まれていたことが判明しました。モデルがこれらの条件を「見たことがない」ため、当然認識できなかったのです。
(2) データ駆動の解決策
Charlie は言いました。「それは 'Garbage In, Garbage Out' だ―AI は与えたものしか学習できない。アルゴリズムのパラメータを調整するより、まずデータ分布を直すべきだ。」
▶ サンプル:データ分布の確認―偏りの発見(難易度:⭐⭐)
# データ分布の確認 — あらゆる AI プロジェクトの最初のステップ
import pandas as pd
df = pd.DataFrame({
'weather': ['sunny'] * 70 + ['rainy'] * 15 + ['foggy'] * 10 + ['night'] * 5,
'label': ['car'] * 50 + ['car'] * 5 + ['car'] * 3 + ['car'] * 2 +
['truck'] * 20 + ['truck'] * 10 + ['truck'] * 7 + ['truck'] * 3
})
print("天候の分布:")
print(df['weather'].value_counts())
print(f"\n晴天画像: {70/100*100:.0f}% — これは問題です!")
天候の分布:
sunny 70
rainy 15
foggy 10
night 5
Name: weather, dtype: int64
晴天画像: 70% — これは問題です!
(3) 効果:データファーストの考え方
Bob が各天候の画像を追加すると、精度は 60% から 85% に上がりました。彼は一つの鉄則を得ました:「モデルを調整するより、データを調整する方が効果的だ。」
3. データ型
(1) 構造による分類
| 型 | 説明 | 例 | AI の処理方法 |
|---|---|---|---|
| 構造化 | 固定された形式(行と列)がある | CSV、SQL テーブル、Excel | 伝統的 ML モデルに直接入力 |
| 半構造化 | 部分的に構造化されているが形式は柔軟 | JSON、XML、HTML、ログ | フィールドを抽出してからモデルに入力 |
| 非構造化 | 固定形式がない | 画像、音声、動画、自然言語 | 深層学習(CNN/RNN/Transformer) |
(2) 数値型による分類
| 型 | 説明 | 例 | モデルの処理 |
|---|---|---|---|
| 数値型 | 連続または離散の数値 | 価格、面積、年齢 | そのまま使用 |
| カテゴリ型 | 有限個の離散値 | 性別、都市、職業 | 符号化が必要(One-Hot) |
| テキスト型 | 自然言語の文字列 | コメント、メール本文 | 埋め込み(embedding)が必要 |
| 時系列型 | 日付と時刻 | 注文時刻、ログ時刻 | 特徴抽出(年/月/時) |
4. 特徴とラベル
特徴はモデルへの入力であり、ラベルはモデルが予測するターゲットです。
graph LR
A[特徴1<br/>面積 120 平方m] --> C[AI モデル]
B[特徴2<br/>部屋数 4] --> C
D[特徴3<br/>立地スコア 8] --> C
C --> E[ラベル<br/>価格 $350,000]
(1) 特徴とは?
特徴とは、データの中で「予測に役立つ」属性です:
| データセット | 考えられる特徴 | ラベル |
|---|---|---|
| 住宅価格データ | 広さ、部屋数、立地評価、階数 | 物件価格 |
| メールデータ | 送信者、キーワード頻度、添付ファイル数 | 迷惑メールか? |
| ユーザーデータ | 年齢、ページビュー数、購入履歴 | 購入の可能性 |
| 画像データ | 画素値(深層学習が自動抽出する特徴) | 画像カテゴリ |
(2) 特徴エンジニアリング―手動 vs 自動抽出
▶ サンプル:手動特徴エンジニアリング―テキストからの特徴抽出(難易度:⭐⭐)
# 例:手動特徴エンジニアリング
import pandas as pd
df = pd.DataFrame({
'text': ['Free iPhone winner!', 'Meeting at 3pm', 'URGENT: Claim now'],
'label': ['spam', 'ham', 'spam']
})
# 手動特徴抽出:特定単語をカウント
df['has_free'] = df['text'].str.lower().str.contains('free').astype(int)
df['has_urgent'] = df['text'].str.lower().str.contains('urgent').astype(int)
df['exclamation_count'] = df['text'].str.count('!')
df['text_length'] = df['text'].str.len()
print(df)
text label has_free has_urgent exclamation_count text_length
0 Free iPhone winner! spam 1 0 1 19
1 Meeting at 3pm ham 0 0 0 14
2 URGENT: Claim now spam 0 1 0 17
5. データセット分割
AI モデルを学習する際、データは互いに排他的な 3 つの集合に分割しなければなりません:
graph TB
A[全データセット<br/>1000 件] --> B[学習セット<br/>800 件<br/>80%]
A --> C[検証セット<br/>100 件<br/>10%]
A --> D[テストセット<br/>100 件<br/>10%]
B --> E[モデルを学習]
C --> F[ハイパーパラメータを調整]
D --> G[最終評価<br/>学習中は使用しない]
| 集合 | 操作 | 比率 | 重要なルール |
|---|---|---|---|
| 学習セット | モデルを学習(パラメータを学習) | 60〜80% | モデルはこの集合から直接学習 |
| 検証セット | ハイパーパラメータ調整(最適構成の選択) | 10〜20% | モデルに間接的に関与するが直接学習はしない |
| テストセット | 最終評価(報告される性能) | 10〜20% | 学習中に使用してはならない |
(1) Python でのデータ分割
▶ サンプル:学習・検証・テストセットへの分割(難易度:⭐⭐)
# データセットを学習/検証/テストに分割
from sklearn.model_selection import train_test_split
import pandas as pd
# サンプルデータセット
df = pd.DataFrame({
'area_sqm': [50, 80, 120, 65, 200, 90, 55, 110, 75, 150,
60, 85, 130, 95, 180, 70, 100, 45, 140, 105],
'price_usd': [150000, 280000, 350000, 220000, 500000, 260000,
165000, 320000, 240000, 420000, 175000, 270000,
380000, 290000, 460000, 210000, 300000, 135000,
400000, 310000]
})
# 第 1 分割:80% を学習+検証、20% をテスト
train_val, test = train_test_split(df, test_size=0.2, random_state=42)
# 第 2 分割:train_val の 75% = 全体の 60% 学習、25% = 全体の 20% 検証
train, val = train_test_split(train_val, test_size=0.25, random_state=42)
print(f"全データセット: {len(df)} 件")
print(f"学習セット: {len(train)} 件 ({len(train)/len(df)*100:.0f}%)")
print(f"検証セット: {len(val)} 件 ({len(val)/len(df)*100:.0f}%)")
print(f"テストセット: {len(test)} 件 ({len(test)/len(df)*100:.0f}%)")
全データセット: 20 件
学習セット: 12 件 (60%)
検証セット: 4 件 (20%)
テストセット: 4 件 (20%)
6. データ品質 ― Garbage In, Garbage Out
データ品質は AI 性能の上限を直接決定します。アルゴリズムがどれほど優れていても、粗悪なデータを補うことはできません:
| 品質問題 | 説明 | 影響 | 検査方法 |
|---|---|---|---|
| 欠損値 | 一部のフィールドが空 | モデルは欠損入力を処理できない | df.isnull().sum() |
| 重複データ | 同じレコードが複数回出現 | モデルが重複サンプルに過学習 | df.duplicated().sum() |
| クラス不均衡 | 一つのクラスが他より遥かに多い | モデルが多数クラスを優先 | df[label].value_counts() |
| ノイズ/誤り | データ値が明らかに不合理 | モデルが誤ったパターンを学習 | 統計的外れ値検出 |
| ラベル付け誤り | 不正なラベル付け | モデルが誤ったマッピングを学習 | 手動スポットチェック |
(1) データ品質問題の特定
▶ サンプル:データ品質検査レポート(難易度:⭐⭐)
# データ品質チェックリスト
import pandas as pd
import numpy as np
df = pd.DataFrame({
'age': [25, 30, np.nan, 45, 200, 28, 30, 30, 35, np.nan],
'income': [50000, 60000, 45000, np.nan, 80000, 55000, 60000, 62000, 70000, 48000],
'label': ['buy', 'no', 'buy', 'no', 'buy', 'no', 'no', 'no', 'buy', 'no']
})
print("=== データ品質レポート ===")
print(f"総行数: {len(df)}")
print(f"\n列ごとの欠損値:")
print(df.isnull().sum())
print(f"\n重複行: {df.duplicated().sum()}")
print(f"\nラベル分布:")
print(df['label'].value_counts())
print(f"\n不審な値 (age > 120):")
print(df[df['age'] > 120])
=== データ品質レポート ===
総行数: 10
列ごとの欠損値:
age 2
income 1
label 0
dtype: int64
重複行: 0
ラベル分布:
no 6
buy 4
Name: label, dtype: int64
不審な値 (age > 120):
age income label
4 200 80000 buy
7. よく使われる公開データセット
AI 実験に自前のデータ収集が必須とは限りません;多くの高品質な公開データセットがすぐに使えます:
| データセット | タスク | サンプル数 | 特徴 |
|---|---|---|---|
| Iris | 分類 | 150 | 最もシンプルな入門用 ML データセット |
| Titanic | 分類 | 891 | 古典的な Kaggle 入門チャレンジ |
| MNIST | 画像分類 | 70,000 | 手書き数字、コンピュータビジョン入門 |
| CIFAR-10 | 画像分類 | 60,000 | 10 クラスのカラー画像;MNIST より難易度が高い |
| California Housing | 回帰 | 20,640 | カリフォルニアの住宅価格予測 |
| IMDb Reviews | 感情分析 | 50,000 | 映画レビューのポジ/ネガ分類 |
8. 総合例:Titanic データ準備パイプライン
Titanic データセットで、以下の手順を完了します:データ概要 → 欠損値チェック → カテゴリ分布統計 → 学習/テスト分割。これで完全な「データ準備パイプライン」を構成します:
▶ サンプル:Titanic データセットの完全なデータ準備パイプライン(難易度:⭐⭐⭐)
# ============================================
# 完全なデータ準備パイプライン
# データセット: Titanic(シミュレート)
# ============================================
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
# Titanic 風のシミュレートデータセット
np.random.seed(42)
df = pd.DataFrame({
'pclass': np.random.choice([1, 2, 3], 100, p=[0.2, 0.3, 0.5]),
'sex': np.random.choice(['male', 'female'], 100, p=[0.6, 0.4]),
'age': np.where(np.random.rand(100) > 0.15,
np.random.randint(1, 80, 100), np.nan),
'fare': np.round(np.random.uniform(10, 500, 100), 2),
'survived': np.random.choice([0, 1], 100, p=[0.6, 0.4])
})
# ステップ 1: 概要
print("=== ステップ1: データ概要 ===")
print(f"Shape: {df.shape}")
print(df.head(5))
# ステップ 2: 欠損値
print("\n=== ステップ2: 欠損値 ===")
missing = df.isnull().sum()
missing_pct = (df.isnull().sum() / len(df) * 100).round(1)
print(pd.DataFrame({'count': missing, 'percent': missing_pct}))
# ステップ 3: ラベル分布
print("\n=== ステップ3: ラベル分布 ===")
print(df['survived'].value_counts())
print(f"生存率: {df['survived'].mean()*100:.1f}%")
# ステップ 4: 学習/テスト分割
train, test = train_test_split(df, test_size=0.2, random_state=42,
stratify=df['survived'])
print(f"\n=== ステップ4: データ分割 ===")
print(f"学習: {len(train)} 件, 生存率: {train['survived'].mean()*100:.1f}%")
print(f"テスト: {len(test)} 件, 生存率: {test['survived'].mean()*100:.1f}%")
print(f"\n層化分割により両集合で同じ生存率を確保!")
=== ステップ1: データ概要 ===
Shape: (100, 5)
pclass sex age fare survived
0 3 female 47.0 339.89 1
1 3 female 63.0 361.38 0
2 1 female 44.0 309.18 0
3 3 male 28.0 385.59 0
4 1 male 8.0 477.95 0
=== ステップ2: 欠損値 ===
count percent
pclass 0 0.0
sex 0 0.0
age 14 14.0
fare 0 0.0
survived 0 0.0
=== ステップ3: ラベル分布 ===
0 58
1 42
Name: survived, dtype: int64
生存率: 42.0%
=== ステップ4: データ分割 ===
学習: 80 件, 生存率: 42.5%
テスト: 20 件, 生存率: 40.0%
層化分割により両集合で同じ生存率を確保!
❓ よくある質問
📖 まとめ
- データは AI の燃料;高品質なデータがなければ良いモデルはない―Garbage In, Garbage Out
- データは 3 つに分類:構造化(表)、半構造化(JSON/XML)、非構造化(画像/テキスト/音声)
- 特徴はモデルの入力、ラベルはモデルが予測するターゲット;特徴エンジニアリングは伝統的機械学習の核心技術
- データセットは学習・検証・テストに分割しなければならない;テストセットは学習中に絶対に使用しない
- データ品質の問題(欠損値、重複、偏り、ノイズ、不正ラベル)は上流工程で解決すべき;モデル学習では悪いデータを直せない
- 公開データセット(Iris、Titanic、MNIST)は AI 学習を始めるのに最適な場所
📝 練習問題
- 基本問題(難易度 ⭐): Python で CSV データセット(または sklearn 付属データセット)を読み込み、
df.info()とdf.describe()で基本情報をまとめなさい。 - 応用問題(難易度 ⭐⭐): 欠損値を確認し、データ内のカテゴリ分布を調べ、特定の列のヒストグラムを描きなさい(ヒント:
df[column].hist())。 - 挑戦問題(難易度:⭐⭐⭐):
train_test_splitで 80/20 の割合に分割し、標準分割と層化分割でカテゴリ比率が同じになるか比較しなさい。