温湿度ログをscikit-learnで学習してエアコンを自動制御する方法【ランダムフォレスト×快適判定】
「温湿度センサーのデータが溜まってきたけど、それをAIで活用するにはどうすればいいの?」
そんな疑問を持っている方に向けた記事です。今回は蓄積した温湿度ログをscikit-learnのランダムフォレストで学習させ、「快適/不快」を自動判定してエアコン制御に繋げるところを徹底的に掘り下げます 🤖🌡️
センサーの配線やCSVへのデータ収集方法は別記事で詳しく解説しているので、本記事では「ラベル付きデータの作り方 → モデル学習 → 精度評価 → 推論結果に基づく制御判断」というAI判定パートに全力を注ぎます。
📋 この記事でわかること・対象読者

- ✅ Pythonの基本構文が書ける初〜中級者の方
- ✅ 温湿度CSVが手元にあり、それをAIで使いたい方
- ✅ scikit-learnを初めて使う・使い始めたばかりの方
- ✅ 機械学習の「学習→評価→推論」の流れを実装で理解したい方
難易度の目安は ⭐⭐⭐(中級) です。
センサーの配線方法・Raspberry Piのセットアップ・CSVへのデータ収集については以下の記事で詳しく解説しています。まだ準備ができていない方はそちらを先にご覧ください。
→ Raspberry Pi+PythonでDHT11温湿度センサーを動かすIoT入門ガイド
→ Raspberry Pi + PythonでIoTダッシュボードを自作|DHT11温湿度センサーで自宅をリアルタイム監視
🧩 全体の流れをつかもう
本記事で扱う処理の流れはこの4ステップです。
- ラベル付きデータを作る(CSVにcomfortラベルを追加)
- ランダムフォレストで学習する
- 精度を評価する(混同行列・分類レポート)
- 推論結果に基づいて制御判断を設計する
「センサーからCSVに保存するまで」は前提として完了している想定で進めます。手元に sensor_log.csv がある状態からスタートしましょう。
本記事のコードで使うライブラリを、先にまとめてインストールしておきます。
pip3 install pandas scikit-learn matplotlib seaborn
🏷️ STEP1:ラベル付きデータの作り方
機械学習の肝は「正解データ(ラベル)」です。温湿度の数値だけでは「快適か不快か」はわかりません。まずは人間が正解を教えてあげる必要があります。
ラベルの定義方針
今回は2クラス分類で進めます。
0:快適(エアコン不要)1:不快(エアコンONすべき)
「不快」の定義は家庭や体感によって異なりますが、一般的な目安として以下のルールベースを出発点にするとラベル付けしやすいです。
| 条件 | ラベル |
|---|---|
| 温度 ≥ 28℃ かつ 湿度 ≥ 70% | 1(不快) |
| 温度 ≥ 30℃ | 1(不快) |
| 温度 22〜27℃ かつ 湿度 40〜65% | 0(快適) |
| 温度 ≤ 18℃ | 1(不快) |
このルールでCSVにラベルを自動付与するスクリプトがこちらです。
import pandas as pd
def add_comfort_label(input_csv='sensor_log.csv', output_csv='sensor_log_labeled.csv'):
"""
温湿度CSVにルールベースでcomfortラベルを追加する。
最初のラベル付けに使い、あとで手動修正も可能。
"""
df = pd.read_csv(input_csv)
# ルールベースでラベルを決定(1=不快、0=快適)
conditions_uncomfortable = (
(df['temperature'] >= 28) & (df['humidity'] >= 70) |
(df['temperature'] >= 30) |
(df['temperature'] <= 18)
)
df['label'] = conditions_uncomfortable.astype(int)
df.to_csv(output_csv, index=False)
print(f'✅ ラベル付きCSVを保存しました: {output_csv}')
print(df['label'].value_counts().rename({0: '快適(0)', 1: '不快(1)'}).to_string())
add_comfort_label()
実行すると sensor_log_labeled.csv が生成され、各行に label 列が追加されます。
ラベルを手動で補正する重要性
ルールベースはあくまで「初期ラベル」です。実際に過ごしてみて「この時間は快適だったのにラベル1になってる」と感じたら、CSVを直接編集して修正しましょう。自分の体感に近いラベルほど、学習後のモデルも実用的になります。
🌳 STEP2:ランダムフォレストで学習する
ラベル付きデータが揃ったら、いよいよ機械学習モデルを作ります。今回使うのはランダムフォレスト(RandomForestClassifier)です。
ランダムフォレストを選ぶ理由は3つあります。
- 数百〜数千行程度の少ないデータでも安定した精度が出やすい
- 温度・湿度のどちらが判定に効いているか特徴量の重要度を確認できる
- ハイパーパラメータのチューニングをしなくてもそこそこ動く
import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report, confusion_matrix
import pickle
def train_comfort_model(csv_file='sensor_log_labeled.csv'):
"""
ラベル付きCSVからランダムフォレストモデルを学習する。
temperature・humidity を特徴量、label を正解ラベルとして使用。
"""
df = pd.read_csv(csv_file)
# 欠損値の除去
df = df.dropna(subset=['temperature', 'humidity', 'label'])
X = df[['temperature', 'humidity']]
y = df['label'].astype(int)
print(f'📊 データ件数: {len(df)}件 (快適: {(y==0).sum()}件 / 不快: {(y==1).sum()}件)')
# 学習用・テスト用に8:2で分割(random_stateで再現性を確保)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
# ランダムフォレストで学習
model = RandomForestClassifier(
n_estimators=100, # 決定木の本数
max_depth=5, # 過学習を防ぐために深さを制限
random_state=42
)
model.fit(X_train, y_train)
# 精度を確認
accuracy = model.score(X_test, y_test)
print(f'\n🎯 テストデータ精度: {accuracy * 100:.1f}%')
# 学習済みモデルを保存
with open('comfort_model.pkl', 'wb') as f:
pickle.dump(model, f)
print('💾 モデルを保存しました: comfort_model.pkl')
return model, X_test, y_test
model, X_test, y_test = train_comfort_model()
ここで stratify=y を指定しているのがポイントです。クラスの偏り(快適データが多い・少ないなど)を学習用とテスト用で同じ比率に保つことで、評価結果が偏らなくなります。
📊 STEP3:精度を正しく評価する
「精度90%!」と聞くと良さそうに見えますが、クラスが偏っているデータでは正解率だけでは不十分です。たとえば「快適」ばかりのデータセットでモデルが全部「快適」と答えれば精度は高くなりますが、まったく役に立ちません。
混同行列と分類レポートで詳しく確認しましょう。なお、グラフの日本語ラベルが「□」(豆腐)に文字化けしないよう、日本語フォント対応の japanize-matplotlib もインストールしておきます。
pip3 install japanize-matplotlib
from sklearn.metrics import classification_report, confusion_matrix
import matplotlib
matplotlib.use('Agg') # GUI不要環境向け
import matplotlib.pyplot as plt
import japanize_matplotlib # 日本語ラベルの文字化け対策
import seaborn as sns
def evaluate_model(model, X_test, y_test):
"""
混同行列と分類レポートでモデルの精度を詳しく評価する。
"""
y_pred = model.predict(X_test)
# 分類レポート(precision・recall・f1-scoreを確認)
print('\n📋 分類レポート:')
print(classification_report(y_test, y_pred, target_names=['快適(0)', '不快(1)']))
# 混同行列
cm = confusion_matrix(y_test, y_pred)
print('\n🔢 混同行列:')
print(cm)
# 混同行列をヒートマップで可視化
fig, ax = plt.subplots(figsize=(5, 4))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
xticklabels=['快適(予測)', '不快(予測)'],
yticklabels=['快適(正解)', '不快(正解)'], ax=ax)
ax.set_title('混同行列:快適/不快の判定結果')
ax.set_ylabel('実際のラベル')
ax.set_xlabel('予測ラベル')
plt.tight_layout()
plt.savefig('confusion_matrix.png', dpi=100)
print('\n📈 混同行列を confusion_matrix.png に保存しました')
# 特徴量の重要度を確認
importances = model.feature_importances_
print(f'\n🌡️ 特徴量の重要度:')
print(f' temperature : {importances[0]:.3f}')
print(f' humidity : {importances[1]:.3f}')
evaluate_model(model, X_test, y_test)
評価指標の読み方
分類レポートには以下の指標が出力されます。エアコン制御においては「不快なのに快適と判定してしまう(見逃し)」が一番困るため、不快クラスのrecall(再現率)を重視して確認しましょう。
| 指標 | 意味 | 制御への影響 |
|---|---|---|
| precision(適合率) | 「不快」と予測したうち本当に不快な割合 | 無駄なエアコンONを減らしたい時に注目 |
| recall(再現率) | 実際に不快な状況をどれだけ検出できたか | 見逃しを防ぎたい時に最重視 |
| f1-score | precisionとrecallの調和平均 | バランスよく評価したい時に使う |
「不快クラスのrecallが0.7以下」なら、ラベルの見直しやデータ追加を検討してみましょう。
🤖 STEP4:推論結果に基づく制御判断を設計する
モデルの精度が確認できたら、いよいよ「推論結果をどう制御ロジックに落とすか」を設計します。ここが実用化の核心です。
単純なON/OFFではなく「確信度」で制御する
ランダムフォレストは predict_proba() で確率(確信度)も取得できます。「60%不快」なら様子見、「90%不快」なら確実にONという段階的な制御が実現できます。
import pickle
import numpy as np
def predict_comfort_with_confidence(temperature, humidity, threshold=0.7):
"""
保存済みモデルで快適度を推論し、確信度に応じた制御指示を返す。
threshold: この確率を超えたら「不快」とみなしてエアコンONを指示する
"""
with open('comfort_model.pkl', 'rb') as f:
model = pickle.load(f)
features = np.array([[temperature, humidity]])
# クラス予測(0=快適, 1=不快)
predicted_class = model.predict(features)[0]
# 確率を取得([快適の確率, 不快の確率])
proba = model.predict_proba(features)[0]
prob_uncomfortable = proba[1]
print(f'\n🌡️ 入力値: 温度 {temperature}℃ / 湿度 {humidity}%')
print(f' 不快確率: {prob_uncomfortable * 100:.1f}%')
# 確信度に応じて制御指示を段階的に決定
if prob_uncomfortable >= threshold:
action = 'AIRCON_ON'
reason = f'不快確率 {prob_uncomfortable*100:.0f}% ≥ 閾値 {threshold*100:.0f}%'
elif prob_uncomfortable >= 0.4:
action = 'STANDBY'
reason = '境界付近のため待機(次の計測で再判断)'
else:
action = 'AIRCON_OFF'
reason = f'快適確率 {(1-prob_uncomfortable)*100:.0f}% が高い'
print(f' 制御指示: {action} 理由: {reason}')
return action, prob_uncomfortable
# 使用例
predict_comfort_with_confidence(28.5, 75.0)
predict_comfort_with_confidence(26.0, 60.0)
predict_comfort_with_confidence(27.5, 68.0) # 境界付近
制御判断フローの設計ポイント
実際の自動制御に組み込む際は、以下の点を考慮した設計にすると誤動作を減らせます。
- ヒステリシス(チャタリング防止):「不快→ON」にしたあと、すぐ「快適→OFF」しないよう、直前の状態を保持して連続判断する
- 連続N回不快で初めてON:1回の測定だけで動かさず、3回連続で不快判定が出たらONにするなど誤動作を防ぐ
- 時刻・曜日の考慮:深夜帯はONしない、平日のみ動作するなどのルールを制御ロジックに追加する
from collections import deque
class ComfortController:
"""
連続N回の判定結果をもとにエアコンON/OFFを判断するクラス。
単発の誤判定でエアコンが動く誤動作を防ぐ。
"""
def __init__(self, consecutive_required=3, threshold=0.7):
self.required = consecutive_required # 何回連続で不快ならONにするか
self.threshold = threshold
self.history = deque(maxlen=consecutive_required) # 直近N回の結果を保持
self.current_state = 'OFF'
def update(self, temperature, humidity):
action, prob = predict_comfort_with_confidence(
temperature, humidity, self.threshold
)
self.history.append(action)
# 直近N回がすべてACON_ONなら本当にONにする
if list(self.history).count('AIRCON_ON') == self.required:
if self.current_state != 'ON':
self.current_state = 'ON'
print('🔴 エアコンONを実行します(連続判定クリア)')
self._execute_aircon(True)
elif list(self.history).count('AIRCON_OFF') == self.required:
if self.current_state != 'OFF':
self.current_state = 'OFF'
print('🟢 エアコンOFFを実行します(連続判定クリア)')
self._execute_aircon(False)
def _execute_aircon(self, turn_on):
"""
実際のリレー制御はここに実装する。
詳細は別記事のリレー制御ガイドを参照。
"""
# GPIO制御の実装は別記事を参照
print(f' GPIO制御: エアコン {"ON" if turn_on else "OFF"}')
# 使用例
controller = ComfortController(consecutive_required=3)
controller.update(28.5, 75.0)
controller.update(29.0, 78.0)
controller.update(29.5, 80.0) # 3回連続でON判定 → エアコンON実行
上記コードの
_execute_aircon() 内でのGPIO・リレー制御の詳細実装、およびLINEへの通知送信については以下の記事で解説しています。
→ Raspberry PiとPythonで作るスマートホームIoT:家電を自動制御してみよう
→ Raspberry PiとPythonで作るIoTスマートホーム:温湿度センサーをLINEに通知する方法
📈 STEP5:データを増やしてモデルを育てる
機械学習モデルはデータが増えるほど精度が上がります。最初は数日分のデータで学習し、徐々にデータを蓄積してモデルを更新していく運用が現実的です。
def update_model_with_new_data(new_csv='sensor_log_labeled.csv'):
"""
新しいラベル付きデータでモデルを再学習し、
comfort_model.pkl を上書き保存する。
定期的(週1回など)に実行してモデルを育てる。
"""
print('🔄 モデルの再学習を開始します...')
model, X_test, y_test = train_comfort_model(new_csv)
evaluate_model(model, X_test, y_test)
print('✅ モデルの更新が完了しました')
運用サイクルのイメージはこうです。
- センサーが毎分データを取得・CSV蓄積
- 週末に新しいデータにラベルを付けて
sensor_log_labeled.csvに追記 update_model_with_new_data()を実行してモデルを更新- 精度評価を確認してラベルの偏りや異常値を修正
🔧 よくあるつまずきポイントと対処法
「データが少なすぎる」問題
ランダムフォレストでも100件未満だと精度が安定しません。最低でも200〜300件、できれば500件以上集まってから学習させましょう。60秒間隔なら1日で1440件貯まります。
「クラスが偏っている」問題
快適データが圧倒的に多い夏のデータだと不快クラスのrecallが低くなりがちです。class_weight='balanced' を追加することで偏りを補正できます。
model = RandomForestClassifier(
n_estimators=100,
max_depth=5,
class_weight='balanced', # クラスの偏りを自動補正
random_state=42
)
「モデルファイルがない」エラー
comfort_model.pkl が存在しない状態で推論しようとするとエラーになります。初回は必ず train_comfort_model() を実行してから推論を呼び出してください。
📝 まとめ
今回の記事では、温湿度ログをscikit-learnで学習させてエアコン自動制御に活用するAIパートを徹底的に解説しました。
- ✅ ルールベース+手動修正でラベル付きデータを作る
- ✅ ランダムフォレストで少ないデータでも実用的なモデルを学習する
- ✅ 分類レポート・混同行列で不快クラスのrecallを重点評価する
- ✅
predict_proba()の確信度と連続判定で誤動作を防ぐ制御ロジックを設計する - ✅ データを週単位で追加してモデルを育てる運用サイクルを作る
センサーのセットアップから始めたい方、リレー制御やLINE通知まで全部つなげたい方は、以下の関連記事も合わせてご活用ください 🔗
- → Raspberry Pi+PythonでDHT11温湿度センサーを動かすIoT入門ガイド(センサー配線・CSV収集)
- → Raspberry PiとPythonで作るスマートホームIoT:家電を自動制御してみよう(リレー制御)
- → Raspberry PiとPythonで作るIoTスマートホーム:温湿度センサーをLINEに通知する方法(LINE通知)
こちらも読まれています
📡 Arduinoをもっと深く学ぼう!
Arduino・ラズパイ・ロボットプログラミングを体系的に学びたい方へ。おすすめのUdemyコースや電子部品もまとめています。
📚 関連商品・おすすめ書籍
※本記事にはアフィリエイトリンクが含まれます。





