大規模モデルの学習は数時間から数日かかることがあり、実行中に通信エラーやOOMなどの要因で停止するリスクが常にあります。監視を放置するとGPUリソースの無駄遣いにつながるため、異常を検知して即座に通知する仕組みが必要です。ここでは、Pythonで実装する2種類の監視方法を介します。
方法1: ログファイルのサイズ変化で学習状態を監視
nohup などで起動した学習プロセスは、通常、標準出力をログファイルに書き出します。正常に学習が進んでいればログは継続的に増えるため、ファイルサイズが一定期間変化しなければ停止を疑うことができます。以下のスクリプトは、Pathlibを使ったサイズ取得と、設定をdataclassに切り出した実装例です。
import time
import smtplib
from pathlib import Path
from email.mime.text import MIMEText
from dataclasses import dataclass
@dataclass
class AlertConfig:
smtp_host: str = "smtp.example.com"
smtp_port: int = 587
account: str = "送信元メールアドレス"
app_password: str = "アプリパスワード"
sender: str = "送信元メールアドレス"
receiver: str = "通知先メールアドレス"
subject: str = "学習プロセス異常検知通知"
CONFIG = AlertConfig()
LOG_PATH = Path("/path/to/nohup.out")
def dispatch_alert(body: str, title: str | None = None) -> None:
subject = title or CONFIG.subject
msg = MIMEText(body)
msg["From"] = CONFIG.sender
msg["To"] = CONFIG.receiver
msg["Subject"] = subject
with smtplib.SMTP(CONFIG.smtp_host, CONFIG.smtp_port) as conn:
conn.starttls()
conn.login(CONFIG.account, CONFIG.app_password)
conn.send_message(msg)
def fetch_log_size() -> int:
try:
return LOG_PATH.stat().st_size
except FileNotFoundError:
return -1
def watch_log(interval_sec: int = 600) -> None:
previous = fetch_log_size()
if previous < 0:
raise FileNotFoundError(f"ログファイルが見つかりません: {LOG_PATH}")
while True:
time.sleep(interval_sec)
current = fetch_log_size()
if current == previous:
dispatch_alert(
f"{interval_sec}秒間でログファイルのサイズが変化しませんでした。"
"学習プロセスが停止している可能性があります。"
)
else:
print(f"ログ増加量: {current - previous} bytes")
previous = current
if __name__ == "__main__":
watch_log()
ポイントは、os.path.getsize ではなく Path.stat().st_size を使い、ファイル存在時の例外も明示的にハンドリングしている点です。監視間隔は要件に応じて調整してください。
方法2: GPUメモリ使用率で学習状態を監視
学習中のGPUは通常、ほぼフルにVRAMを消費します。全GPUの合計使用率が急激に低下した場合も、プロセスの停止や例外発生を示唆します。ここでは nvidia-ml-py を使い、各デバイスのメモリ使用率を直接取得する実装に変更しています。
import time
import smtplib
from email.mime.text import MIMEText
from dataclasses import dataclass
from pynvml import (
nvmlInit,
nvmlShutdown,
nvmlDeviceGetCount,
nvmlDeviceGetHandleByIndex,
nvmlDeviceGetMemoryInfo,
)
@dataclass
class MonitorSetting:
smtp_host: str = "smtp.example.com"
smtp_port: int = 587
account: str = "送信元メールアドレス"
app_password: str = "アプリパスワード"
sender: str = "送信元メールアドレス"
receiver: str = "通知先メールアドレス"
subject: str = "GPU利用率低下通知"
SETTING = MonitorSetting()
LOW_MEMORY_THRESHOLD = 10.0 # 使用率(%)しきい値
CHECK_INTERVAL = 600 # 秒
def send_notification(body: str, subject: str | None = None) -> None:
msg = MIMEText(body)
msg["From"] = SETTING.sender
msg["To"] = SETTING.receiver
msg["Subject"] = subject or SETTING.subject
with smtplib.SMTP(SETTING.smtp_host, SETTING.smtp_port) as conn:
conn.starttls()
conn.login(SETTING.account, SETTING.app_password)
conn.send_message(msg)
def aggregate_gpu_usage() -> float:
nvmlInit()
device_count = nvmlDeviceGetCount()
total_used, total_all = 0.0, 0.0
try:
for idx in range(device_count):
handle = nvmlDeviceGetHandleByIndex(idx)
mem_info = nvmlDeviceGetMemoryInfo(handle)
total_used += mem_info.used
total_all += mem_info.total
finally:
nvmlShutdown()
return (total_used / total_all) * 100 if total_all > 0 else 0.0
def monitor_gpu() -> None:
while True:
usage_percent = aggregate_gpu_usage()
if usage_percent < LOW_MEMORY_THRESHOLD:
send_notification(
f"GPUメモリ総使用率が {usage_percent:.2f}% に低下しました。"
"学習プロセスの停止を疑う必要があります。"
)
else:
print(f"GPUメモリ総使用率: {usage_percent:.2f}%")
time.sleep(CHECK_INTERVAL)
if __name__ == "__main__":
monitor_gpu()
この例では、nvidia-smi のパースを避け、NVIDIA Management Library経由で値を取得しています。これによりフォーマット変更の影響を受けにくく、より安定した監視が可能です。
運用のヒント
- どちらのスクリプトも
nohup python watcher.py &などで常駐させて運用します。 - 閾値や間隔は、学習中の実測値に基づいて調整してください。
- 通知先はメールに限定せず、Slack Incoming Webhookや企業用ChatOpsツールへ置き換えることも容易です。
- 監視プロセス自身の死活も重要なため、systemdサービス化やcronでの重複起動防止を検討するとよいでしょう。