0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[ClaudeCode Skills] 機械学習などの実験レポートを自動生成する

Posted at

背景

仕事や副業で機械学習や画像処理、アルゴリズムの実験を行うことがよくあります。その際、関係者に新たな施策などの実験結果を報告するのですが、そのレポートの作成を手間に感じていました。
そこで、ClaudeCodeのスキル機能を使って、実験レポートの作成を自動化することを考えました。

前提条件

ClaudeCodeをインストールしていること、ログイン済みであり、実験をClaudeCodeを使いながら行うか、またはディレクトリの構造やファイルから実験の経緯や結果がわかること、これらができていれば、多分ClaudeCodeのセッションがコンテキストを理解してくれるはずである。

設定方法

1. MCPサーバーでカスタムスキルを作成

Claude Codeは/mnt/skillsディレクトリからスキルを読み込めます。これが最も強力で再利用可能な方法です。

# スキルディレクトリを作成
~/.claude/skills/experiment-report/
├── SKILL.md
└── templates/
    ├── report_template.md
    └── experiment_checklist.md

SKILL.mdの例

# Reproducible Experiment Report Skill

## Purpose
完全に再現可能な実験レポートを作成する。他の研究者や未来の自分が同じ結果を得られることを保証。

## Report Template

### 1. 実験メタデータ
- **実験ID**: `EXP-YYYYMMDD-NNN`
- **実験名**: 
- **実験日時**: YYYY-MM-DD HH:MM:SS (JST)
- **実験者**: 
- **関連Issue/PR**: #123

### 2. 実験目的
- **背景**: なぜこの実験が必要か
- **仮説**: 検証したい仮説
- **成功基準**: 定量的な判断基準

### 3. 実験環境(再現に必須)

#### 3.1 ハードウェア
```yaml
cpu: 
gpu: 
memory: 
storage: 
```

#### 3.2 ソフトウェア環境
```bash
# OS情報
uname -a

# Python環境
python --version
pip freeze > requirements_YYYYMMDD.txt

# その他重要な依存関係
node --version
docker --version
```

#### 3.3 環境変数
```
export MODEL_PATH=/path/to/model
export BATCH_SIZE=32
export RANDOM_SEED=42
```

### 4. データセット情報
- **データソース**: 
- **データバージョン**: 
- **データサイズ**: 
- **データハッシュ**: `sha256sum dataset.csv`
- **前処理スクリプト**: `scripts/preprocess_data.py`
- **データ分割**: train(70%) / val(15%) / test(15%)

### 5. 実験手順(完全再現用)

#### 5.1 準備ステップ
```bash
# 1. リポジトリのクローン
git clone https://github.com/your-org/project.git
cd project
git checkout <commit-hash>

# 2. 環境構築
python -m venv venv
source venv/bin/activate
pip install -r requirements_YYYYMMDD.txt

# 3. データ準備
python scripts/download_data.py --version 1.2.3
python scripts/preprocess_data.py --config configs/preprocess.yaml
```

#### 5.2 実験実行
```bash
# メインスクリプト実行
python experiments/train_model.py \
  --config experiments/configs/exp_001.yaml \
  --seed 42 \
  --output-dir results/exp_001

# 実行時刻
# Started: 2025-01-03 14:30:00
# Finished: 2025-01-03 16:45:00
# Duration: 2h 15m
```

#### 5.3 使用したスクリプト一覧
```
experiments/
├── train_model.py          # メイン訓練スクリプト (commit: abc123)
├── evaluate.py             # 評価スクリプト
├── configs/
│   └── exp_001.yaml       # 実験設定ファイル
└── utils/
    ├── data_loader.py      # データローダー
    └── metrics.py          # 評価指標計算

scripts/
├── download_data.py        # データダウンロード
├── preprocess_data.py      # 前処理 (version: 2.1.0)
└── visualize_results.py    # 可視化
```

#### 5.4 設定ファイル(exp_001.yaml)
```yaml
model:
  architecture: transformer
  hidden_size: 768
  num_layers: 12
  
training:
  batch_size: 32
  learning_rate: 0.0001
  epochs: 100
  optimizer: adam
  
data:
  train_path: data/processed/train.csv
  val_path: data/processed/val.csv
```

### 6. 実験結果

#### 6.1 定量的結果
```python
# results/exp_001/metrics.json
{
  "accuracy": 0.923,
  "precision": 0.917,
  "recall": 0.931,
  "f1_score": 0.924,
  "training_time_sec": 8100,
  "inference_time_ms": 45.2
}
```

#### 6.2 学習曲線
![Learning Curve](results/exp_001/learning_curve.png)

生成コマンド:
```bash
python scripts/visualize_results.py \
  --input results/exp_001/training_log.csv \
  --output results/exp_001/learning_curve.png
```

#### 6.3 ログファイル
- 完全なログ: `results/exp_001/training.log`
- TensorBoard: `tensorboard --logdir results/exp_001/tensorboard`

### 7. 分析と考察

#### 7.1 結果の解釈
- 仮説との比較
- ベースラインとの差異

#### 7.2 失敗した試み(重要)
```
# 試行1: learning_rate=0.001 → 発散
# 試行2: batch_size=64 → OOM error
# 試行3: optimizer=sgd → 収束が遅い
```

#### 7.3 制約と限界
- データセットのバイアス
- 計算リソースの制約
- 一般化の懸念

### 8. 再現性チェックリスト
- [ ] コミットハッシュを記録
- [ ] requirements.txtを生成
- [ ] ランダムシードを固定
- [ ] データバージョンを記録
- [ ] 設定ファイルを保存
- [ ] 実行コマンドを記録
- [ ] 環境変数を記録
- [ ] 生成されたファイルのハッシュ値を記録

### 9. 成果物とアーティファクト
```
results/exp_001/
├── model_final.pth          # 訓練済みモデル
├── metrics.json             # 評価指標
├── training_log.csv         # 学習ログ
├── config_used.yaml         # 実際に使用した設定
├── predictions.csv          # 予測結果
└── checkpoints/
    ├── epoch_010.pth
    ├── epoch_020.pth
    └── best_model.pth
```

### 10. 次のステップ
- [ ] ハイパーパラメータの最適化
- [ ] より大きなデータセットで検証
- [ ] 本番環境へのデプロイ準備

### 11. 参考資料
- 関連論文: 
- 参考実装: 
- 社内ドキュメント: 

---

## Usage in Claude Code

実験完了後、以下のように依頼してください:
```
この実験の再現可能なレポートを作成してください。
使用したスクリプトは experiments/train_model.py と scripts/preprocess_data.py です。
```

Claude Codeは自動的に:
1. Git commit hashを取得
2. requirements.txtを生成
3. 使用したスクリプトの内容を確認
4. 実行コマンドを記録
5. このテンプレートに従ってレポートを生成

します。

2. プロジェクト設定ファイル(.claude/config.json)

プロジェクトルートに設定ファイルを配置:

{
  "report_template": {
    "sections": [
      "実験概要",
      "実験手順", 
      "結果",
      "分析",
      "次のアクション"
    ],
    "output_format": "markdown",
    "include_code_snippets": true,
    "include_metrics": true
  },
  "custom_instructions": "実験レポート作成時は必ず定量的な指標を含めてください。コードの変更点はdiffフォーマットで記載してください。"
}

セッション開始時にClaudeに読み込ませる:

# 最初のメッセージで
cat .claude/config.json

システムプロンプトファイル(推奨度高)

~/.claude/system_prompt.md を作成:

# 実験レポート作成ガイドライン

実験レポートを作成する際は、以下のフォーマットに従ってください:

## 必須セクション
1. **実験概要** - 目的、仮説、期待結果
2. **実験環境** - OS、ライブラリバージョン、ハードウェア
3. **実験手順** - 再現可能な詳細手順
4. **結果** - 生データ、可視化、統計
5. **考察** - 解釈、制約、次のステップ

## 記載ルール
- 数値は必ず単位を明記
- コードブロックは実行可能な形式で
- グラフは必ずラベルとタイトルを付与
- 失敗した実験も記録(学びとして)

## 出力フォーマット
- Markdown形式
- ファイル名: `YYYYMMDD_experiment_name.md`
- 保存先: `./reports/`

自動化スクリプト(generate_report.py)

#!/usr/bin/env python3
"""
再現可能な実験レポート自動生成スクリプト
Usage: python generate_report.py --experiment-dir results/exp_001
"""

import os
import sys
import json
import subprocess
import hashlib
from datetime import datetime
from pathlib import Path
import yaml

class ExperimentReporter:
    def __init__(self, experiment_dir: str):
        self.exp_dir = Path(experiment_dir)
        self.report_data = {}
        
    def collect_git_info(self):
        """Git情報を収集"""
        try:
            commit = subprocess.check_output(['git', 'rev-parse', 'HEAD']).decode().strip()
            branch = subprocess.check_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD']).decode().strip()
            dirty = subprocess.check_output(['git', 'status', '--porcelain']).decode().strip()
            
            return {
                'commit_hash': commit,
                'branch': branch,
                'has_uncommitted_changes': bool(dirty),
                'uncommitted_files': dirty.split('\n') if dirty else []
            }
        except:
            return {'error': 'Not a git repository'}
    
    def collect_environment_info(self):
        """環境情報を収集"""
        env_info = {
            'python_version': sys.version,
            'platform': sys.platform,
        }
        
        # requirements.txt生成
        try:
            requirements = subprocess.check_output(['pip', 'freeze']).decode()
            req_file = self.exp_dir / 'requirements_snapshot.txt'
            req_file.write_text(requirements)
            env_info['requirements_file'] = str(req_file)
        except:
            pass
        
        # 環境変数
        important_vars = ['CUDA_VISIBLE_DEVICES', 'MODEL_PATH', 'DATA_PATH']
        env_info['environment_variables'] = {
            k: os.getenv(k) for k in important_vars if os.getenv(k)
        }
        
        return env_info
    
    def collect_script_info(self, script_paths: list):
        """使用したスクリプトの情報を収集"""
        scripts = {}
        for script_path in script_paths:
            path = Path(script_path)
            if path.exists():
                # ファイルハッシュ
                content = path.read_bytes()
                file_hash = hashlib.sha256(content).hexdigest()
                
                scripts[str(path)] = {
                    'hash': file_hash,
                    'size': len(content),
                    'modified': datetime.fromtimestamp(path.stat().st_mtime).isoformat()
                }
        return scripts
    
    def collect_data_info(self, data_paths: list):
        """データセット情報を収集"""
        datasets = {}
        for data_path in data_paths:
            path = Path(data_path)
            if path.exists():
                # ファイルハッシュ
                file_hash = hashlib.sha256(path.read_bytes()).hexdigest()
                
                datasets[str(path)] = {
                    'hash': file_hash,
                    'size_mb': path.stat().st_size / (1024 * 1024),
                    'modified': datetime.fromtimestamp(path.stat().st_mtime).isoformat()
                }
        return datasets
    
    def generate_reproduction_script(self):
        """再現用シェルスクリプトを生成"""
        script = f"""#!/bin/bash
# Experiment Reproduction Script
# Generated: {datetime.now().isoformat()}

set -e  # Exit on error

echo "=== Experiment Reproduction ==="
echo "Experiment: {self.exp_dir.name}"

# 1. Clone repository
echo "Step 1: Cloning repository..."
git clone <REPOSITORY_URL> project
cd project
git checkout {self.report_data.get('git', {}).get('commit_hash', 'main')}

# 2. Setup environment
echo "Step 2: Setting up environment..."
python -m venv venv
source venv/bin/activate
pip install -r {self.exp_dir}/requirements_snapshot.txt

# 3. Download data
echo "Step 3: Downloading data..."
# Add your data download commands here

# 4. Run experiment
echo "Step 4: Running experiment..."
# Add your experiment command here

echo "=== Reproduction Complete ==="
"""
        script_path = self.exp_dir / 'reproduce.sh'
        script_path.write_text(script)
        script_path.chmod(0o755)
        return script_path
    
    def generate_report(self, 
                       experiment_name: str,
                       script_paths: list,
                       data_paths: list,
                       config_path: str = None):
        """完全なレポートを生成"""
        
        # 情報収集
        self.report_data = {
            'experiment_id': f"EXP-{datetime.now().strftime('%Y%m%d')}-{self.exp_dir.name}",
            'experiment_name': experiment_name,
            'timestamp': datetime.now().isoformat(),
            'git': self.collect_git_info(),
            'environment': self.collect_environment_info(),
            'scripts': self.collect_script_info(script_paths),
            'datasets': self.collect_data_info(data_paths)
        }
        
        # 設定ファイル読み込み
        if config_path and Path(config_path).exists():
            with open(config_path) as f:
                self.report_data['config'] = yaml.safe_load(f)
        
        # レポート生成
        report_md = self._format_report()
        
        # 保存
        report_path = self.exp_dir / 'EXPERIMENT_REPORT.md'
        report_path.write_text(report_md)
        
        # メタデータJSON
        meta_path = self.exp_dir / 'experiment_metadata.json'
        with open(meta_path, 'w') as f:
            json.dump(self.report_data, f, indent=2, ensure_ascii=False)
        
        # 再現スクリプト
        repro_script = self.generate_reproduction_script()
        
        print(f"✅ Report generated: {report_path}")
        print(f"✅ Metadata saved: {meta_path}")
        print(f"✅ Reproduction script: {repro_script}")
        
        return report_path
    
    def _format_report(self):
        """Markdownレポートをフォーマット"""
        data = self.report_data
        
        report = f"""# {data['experiment_name']}

**実験ID**: `{data['experiment_id']}`  
**実験日時**: {data['timestamp']}  
**Git Commit**: `{data['git'].get('commit_hash', 'N/A')}`

## 1. 実験環境

### Git情報
```yaml
commit: {data['git'].get('commit_hash', 'N/A')}
branch: {data['git'].get('branch', 'N/A')}
uncommitted_changes: {data['git'].get('has_uncommitted_changes', False)}
```

### Python環境
```
{data['environment']['python_version']}
```

Requirements: `{data['environment'].get('requirements_file', 'N/A')}`

### 環境変数
```bash
{self._format_env_vars(data['environment'].get('environment_variables', {}))}
```

## 2. 使用したスクリプト

{self._format_scripts(data['scripts'])}

## 3. データセット

{self._format_datasets(data['datasets'])}

## 4. 実験設定
```yaml
{yaml.dump(data.get('config', {}), allow_unicode=True)}
```

## 5. 再現手順

1. リポジトリをクローン:
```bash
   git clone <REPO_URL>
   git checkout {data['git'].get('commit_hash', 'main')}
```

2. 環境構築:
```bash
   pip install -r {data['environment'].get('requirements_file', 'requirements.txt')}
```

3. 実験実行:
```bash
   # 実行コマンドをここに記載
```

または、自動再現スクリプトを実行:
```bash
bash {self.exp_dir}/reproduce.sh
```

## 6. 結果

*(ここに実験結果を記載)*

## 7. 分析

*(ここに分析を記載)*

## 8. 次のステップ

- [ ] TODO項目をリストアップ

---
*このレポートは自動生成されました*
"""
        return report
    
    def _format_env_vars(self, env_vars):
        return '\n'.join(f"export {k}={v}" for k, v in env_vars.items())
    
    def _format_scripts(self, scripts):
        lines = []
        for path, info in scripts.items():
            lines.append(f"### `{path}`")
            lines.append(f"- SHA256: `{info['hash']}`")
            lines.append(f"- Size: {info['size']} bytes")
            lines.append(f"- Modified: {info['modified']}")
            lines.append("")
        return '\n'.join(lines)
    
    def _format_datasets(self, datasets):
        lines = []
        for path, info in datasets.items():
            lines.append(f"### `{path}`")
            lines.append(f"- SHA256: `{info['hash']}`")
            lines.append(f"- Size: {info['size_mb']:.2f} MB")
            lines.append(f"- Modified: {info['modified']}")
            lines.append("")
        return '\n'.join(lines)


if __name__ == '__main__':
    import argparse
    
    parser = argparse.ArgumentParser(description='Generate reproducible experiment report')
    parser.add_argument('--experiment-dir', required=True, help='Experiment directory')
    parser.add_argument('--name', required=True, help='Experiment name')
    parser.add_argument('--scripts', nargs='+', required=True, help='Script files used')
    parser.add_argument('--data', nargs='+', required=True, help='Data files used')
    parser.add_argument('--config', help='Config file path')
    
    args = parser.parse_args()
    
    reporter = ExperimentReporter(args.experiment_dir)
    reporter.generate_report(
        experiment_name=args.name,
        script_paths=args.scripts,
        data_paths=args.data,
        config_path=args.config
    )

使用例

次のようなプロンプトなどをClaudeCodeに入力して、レポートを作成できます。

スキルを使って今の実験の実験レポートを書いてください。

お願い

私はなんとなく少しずつ設定してプロンプトをうちこんでいくうちにうまくいくようになったので、一発ではうまくいかないかもしれません。
その際は詰まったところやおかしなところは、コメントで共有していただけると幸いです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?