.
This commit is contained in:
@@ -1,38 +1,97 @@
|
||||
import { parseTja } from './parse';
|
||||
import { factorize, Factors } from './factorize';
|
||||
import { factorize } from './factorize';
|
||||
import { join } from 'path';
|
||||
import { readFileSync } from 'fs';
|
||||
import { readFileSync, writeFileSync, unlinkSync, existsSync } from 'fs';
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
async function predict() {
|
||||
const args = process.argv.slice(2);
|
||||
const workingDir = args[0];
|
||||
const scriptDir = args[1];
|
||||
const tjaPath = args[2];
|
||||
const difficulty = (args[3] || 'oni').toLowerCase() as 'oni' | 'edit';
|
||||
/**
|
||||
* 예측 엔진: 모델 실행을 위해 Python 환경을 호출
|
||||
*/
|
||||
function runInference(workingDir: string, factors: Record<string, number>): number {
|
||||
const tempFactorPath = join(workingDir, `temp_feat_${Date.now()}.json`);
|
||||
writeFileSync(tempFactorPath, JSON.stringify(factors));
|
||||
|
||||
if (!tjaPath) {
|
||||
console.error('Usage: bun script/predict.ts <working_dir> <script_dir> <tja_path> <difficulty>');
|
||||
const pythonScript = `
|
||||
import json, torch, sys
|
||||
import numpy as np
|
||||
import torch.nn as nn
|
||||
|
||||
# 모델 정의 (학습된 구조와 동일해야 함)
|
||||
class DifficultyNet(nn.Module):
|
||||
def __init__(self, input_dim):
|
||||
super().__init__()
|
||||
self.net = nn.Sequential(
|
||||
nn.Linear(input_dim, 128),
|
||||
nn.BatchNorm1d(128),
|
||||
nn.ReLU(),
|
||||
nn.Dropout(0.4),
|
||||
nn.Linear(128, 64),
|
||||
nn.ReLU(),
|
||||
nn.Dropout(0.3),
|
||||
nn.Linear(64, 32),
|
||||
nn.ReLU(),
|
||||
nn.Linear(32, 1),
|
||||
nn.Sigmoid()
|
||||
)
|
||||
def forward(self, x): return self.net(x)
|
||||
|
||||
def predict():
|
||||
try:
|
||||
with open('${tempFactorPath}', 'r') as f: factors = json.load(f)
|
||||
with open('${join(workingDir, 'scaler.json')}', 'r') as f: s = json.load(f)
|
||||
|
||||
X = np.array([[factors[col] for col in s['cols']]], dtype=np.float32)
|
||||
|
||||
# 정규화 (Robust/Standard 지원)
|
||||
if 'center' in s: X_scaled = (X - np.array(s['center'])) / np.array(s['scale'])
|
||||
else: X_scaled = (X - np.array(s.get('mean', 0))) / np.array(s.get('std', s.get('scale', 1)))
|
||||
|
||||
model = DifficultyNet(len(s['cols']))
|
||||
model.load_state_dict(torch.load('${join(workingDir, 'model.pth')}', map_location='cpu'))
|
||||
model.eval()
|
||||
|
||||
with torch.no_grad():
|
||||
input_tensor = torch.from_numpy(X_scaled).float()
|
||||
pred = model(input_tensor) * 11 + 1
|
||||
print(float(pred.item()))
|
||||
except Exception as e:
|
||||
print(f"ERROR: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__": predict()
|
||||
`;
|
||||
|
||||
const tempPyPath = join(workingDir, `inference_${Date.now()}.py`);
|
||||
writeFileSync(tempPyPath, pythonScript);
|
||||
|
||||
try {
|
||||
const result = execSync(`python3 ${tempPyPath}`).toString().trim();
|
||||
return parseFloat(result);
|
||||
} finally {
|
||||
if (existsSync(tempFactorPath)) unlinkSync(tempFactorPath);
|
||||
if (existsSync(tempPyPath)) unlinkSync(tempPyPath);
|
||||
}
|
||||
}
|
||||
|
||||
function main() {
|
||||
const [workingDir, tjaPath, difficulty = 'oni'] = process.argv.slice(2);
|
||||
|
||||
if (!workingDir || !tjaPath) {
|
||||
console.log("Usage: bun run script/predict.ts <workingDir> <tjaPath> [difficulty]");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const factorJsonPath = join(scriptDir, 'factor.json');
|
||||
const weights: Factors = JSON.parse(readFileSync(factorJsonPath, 'utf-8'));
|
||||
|
||||
const tjaContent = readFileSync(tjaPath, 'utf-8');
|
||||
const parsed = parseTja(tjaContent);
|
||||
const chart = parsed[difficulty.toLowerCase() as 'oni' | 'edit'];
|
||||
|
||||
if (!parsed || !parsed[difficulty]) {
|
||||
console.error(`Error: Could not find difficulty '${difficulty}' in TJA.`);
|
||||
if (!chart) {
|
||||
console.error(`Error: Difficulty '${difficulty}' not found.`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const factors = factorize(parsed[difficulty]!);
|
||||
const prediction = Object.keys(factors).reduce((sum, key) =>
|
||||
sum + (factors[key as keyof Factors] * weights[key as keyof Factors]), 0);
|
||||
|
||||
console.log(`Prediction for ${tjaPath} (${difficulty}):`);
|
||||
console.log(`- Predicted Value: ${prediction.toFixed(4)}`);
|
||||
console.log('- Factors:', factors);
|
||||
const score = runInference(workingDir, factorize(chart));
|
||||
console.log(`\n🎯 Predicted Difficulty: ${score.toFixed(2)}`);
|
||||
}
|
||||
|
||||
predict();
|
||||
main();
|
||||
|
||||
Reference in New Issue
Block a user