Orchestration du Fine-tuning
Validation de datasets, orchestration d'API et versionnage de modèles
Le package fine-tuning fournit des outils pour orchestrer les jobs de fine-tuning sur plusieurs fournisseurs, valider les datasets, collecter les feedbacks et gérer les versions de modèles.
Installation
npm install @orka-js/finetuning# orpnpm add @orka-js/finetuningFonctionnalités
Validation de Dataset
Validez les datasets JSONL avant l'entraînement
Multi-Fournisseur
OpenAI, Anthropic, Mistral, Together, Anyscale
Estimation des Coûts
Estimez les coûts d'entraînement avant de commencer
Suivi des Jobs
Suivez la progression et les métriques d'entraînement
Versionnage de Modèles
Suivez les versions des modèles fine-tunés
Collecte de Feedback
Collectez les feedbacks utilisateurs et convertissez-les en datasets
Format du Dataset
Le format JSONL attendu pour le fine-tuning :
{"messages": [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Hello"}, {"role": "assistant", "content": "Hi! How can I help?"}]}{"messages": [{"role": "user", "content": "What is 2+2?"}, {"role": "assistant", "content": "2+2 equals 4."}]}#Validation de Dataset
Validez vos datasets JSONL avant de les soumettre pour le fine-tuning.
import { DatasetValidator } from '@orka-js/finetuning'; const validator = new DatasetValidator('openai');const result = await validator.validateFile('./training.jsonl'); if (result.valid) { console.log('Dataset is valid!'); console.log('Stats:', result.stats); // { totalExamples: 100, totalTokens: 50000, avgTokensPerExample: 500, ... }} else { console.log('Errors:', result.errors); // [{ line: 5, message: 'Missing assistant message' }, ...]} // Check warningsif (result.warnings.length > 0) { console.log('Warnings:', result.warnings);}#Orchestration du Fine-tuning
Créez et surveillez les jobs de fine-tuning avec suivi automatique du statut.
import { FineTuningOrchestrator } from '@orka-js/finetuning'; const orchestrator = new FineTuningOrchestrator({ provider: 'openai', baseModel: 'gpt-4o-mini-2024-07-18', apiKey: process.env.OPENAI_API_KEY, hyperparameters: { nEpochs: 3, batchSize: 4, },}); // Estimate cost before trainingconst estimate = orchestrator.estimateCost(100000, 3); // 100k tokens, 3 epochsconsole.log(`Estimated cost: $${estimate.trainingCost.toFixed(2)}`); // Create a fine-tuning jobconst job = await orchestrator.createJob('./training.jsonl', { validationPath: './validation.jsonl',}); console.log('Job created:', job.id); // Monitor progressorchestrator.on((event) => { if (event.type === 'metrics') { console.log('Training loss:', event.metrics?.trainingLoss); } if (event.type === 'message') { console.log('Status:', event.message); }}); // Wait for completionconst completed = await orchestrator.waitForCompletion(job.id);console.log('Fine-tuned model:', completed.fineTunedModel); // Get model versionsconst versions = orchestrator.getModelVersions();const latest = orchestrator.getLatestVersion();#Collecte de Feedback
Collectez les feedbacks utilisateurs de votre application et convertissez-les en données d'entraînement.
import { FeedbackCollector } from '@orka-js/finetuning'; const collector = new FeedbackCollector({ minSamples: 50, filterLowRatings: true, ratingThreshold: 4,}); // Collect feedback from your applicationcollector.add({ input: 'What is the capital of France?', output: 'The capital of France is Paris.', rating: 5,}); collector.add({ input: 'Explain quantum computing', output: 'Quantum computing uses qubits...', expectedOutput: 'Quantum computing is a type of computation...', rating: 3, feedback: 'Could be more detailed',}); // Check statsconst stats = collector.getStats();console.log('Total entries:', stats.total);console.log('Filtered entries:', stats.filtered);console.log('Average rating:', stats.avgRating); // Check if ready for trainingif (collector.isReadyForTraining()) { // Convert to dataset const dataset = collector.toDataset(); // Or save directly to file await collector.saveToFile('./feedback-dataset.jsonl');}Fournisseurs Supportés
Live Ecosystem Compatibility
| Provider | Integration Status | Capability / Models |
|---|---|---|
| OpenAI | Full | GPT-4o, GPT-3.5-turbo, DALL-E |
| Mistral AI | Full | Mixtral, Mistral Large, Codestral |
| Anthropic | Beta | Claude 3.5 Sonnet / Opus |
| Together AI | Planned | Llama 3, Qwen, Mixtral 8x22B |
| Anyscale | Planned | Coming Q3 2026 |
* All providers are monitored 24/7 for API uptime and performance latency.
Exemples Complets
Exemple 1 : Pipeline de Fine-tuning pour Support Client
Workflow complet de la collecte de feedback au déploiement du modèle fine-tuné :
import { FeedbackCollector, DatasetValidator, FineTuningOrchestrator } from '@orka-js/finetuning';import { OpenAIAdapter } from '@orka-js/openai'; // Étape 1 : Collecter les retours utilisateursconst collector = new FeedbackCollector({ storage: 'file', filePath: './feedback.jsonl'}); // Collecter les retours des interactions en productionasync function collectProductionFeedback() { // L'utilisateur pose une question const userInput = "Comment réinitialiser mon mot de passe ?"; const modelOutput = "Cliquez sur 'Mot de passe oublié' sur la page de connexion."; // L'utilisateur note la réponse await collector.addFeedback({ input: userInput, output: modelOutput, rating: 8, correction: "Cliquez sur 'Mot de passe oublié' sur la page de connexion, puis vérifiez votre email pour le lien de réinitialisation.", metadata: { userId: 'user_123', timestamp: new Date().toISOString(), category: 'password_reset' } }); console.log('Feedback collecté. Total:', await collector.getCount());} // Étape 2 : Exporter et valider le datasetasync function prepareDataset() { // Exporter les retours en dataset d'entraînement const dataset = await collector.exportDataset({ format: 'openai', minRating: 7, // Utiliser uniquement les réponses bien notées includeCorrections: true }); console.log(`${dataset.length} exemples d'entraînement exportés`); // Valider le dataset const validator = new DatasetValidator(); const validation = await validator.validate(dataset, { provider: 'openai', checkDuplicates: true, checkBalance: true }); if (!validation.valid) { console.error('Erreurs de validation:', validation.errors); throw new Error('Échec de la validation du dataset'); } console.log('Dataset validé avec succès'); console.log('Statistiques:', validation.stats); return dataset;} // Étape 3 : Estimer le coût et créer le job de fine-tuningasync function createFineTuningJob(dataset: any[]) { const orchestrator = new FineTuningOrchestrator({ provider: 'openai', apiKey: process.env.OPENAI_API_KEY! }); // Estimer le coût avant de commencer const costEstimate = await orchestrator.estimateCost({ dataset, baseModel: 'gpt-3.5-turbo', epochs: 3 }); console.log(`Coût estimé : ${costEstimate.estimatedCost.toFixed(2)}$`); console.log(`Tokens d'entraînement : ${costEstimate.trainingTokens}`); // Créer le job de fine-tuning const job = await orchestrator.createJob({ trainingData: dataset, baseModel: 'gpt-3.5-turbo', suffix: 'customer-support-v1', hyperparameters: { nEpochs: 3, batchSize: 4, learningRateMultiplier: 0.1 } }); console.log('Job de fine-tuning créé:', job.id); return job;} // Étape 4 : Surveiller la progression du jobasync function monitorJob(orchestrator: FineTuningOrchestrator, jobId: string) { const checkInterval = setInterval(async () => { const jobs = await orchestrator.listJobs(); const job = jobs.find(j => j.id === jobId); if (!job) { clearInterval(checkInterval); return; } console.log(`Job ${jobId} statut : ${job.status}`); if (job.status === 'succeeded') { console.log('Fine-tuning terminé !'); console.log('Modèle fine-tuné:', job.fineTunedModel); clearInterval(checkInterval); // Utiliser le modèle fine-tuné await useFineTunedModel(job.fineTunedModel!); } else if (job.status === 'failed') { console.error('Échec du fine-tuning:', job.error); clearInterval(checkInterval); } }, 30000); // Vérifier toutes les 30 secondes} // Étape 5 : Utiliser le modèle fine-tunéasync function useFineTunedModel(modelName: string) { const llm = new OpenAIAdapter({ apiKey: process.env.OPENAI_API_KEY!, model: modelName }); const response = await llm.generate({ messages: [ { role: 'user', content: 'Comment réinitialiser mon mot de passe ?' } ] }); console.log('Réponse du modèle fine-tuné:', response.content);} // Exécuter le pipeline completasync function main() { // Collecter les retours (exécuter en continu en production) await collectProductionFeedback(); // Quand prêt pour le fine-tuning (ex: après 1000+ exemples collectés) const dataset = await prepareDataset(); const job = await createFineTuningJob(dataset); const orchestrator = new FineTuningOrchestrator({ provider: 'openai', apiKey: process.env.OPENAI_API_KEY! }); await monitorJob(orchestrator, job.id);} main().catch(console.error);Exemple 2 : Tests A/B avec Modèles Fine-tunés
Comparer les performances du modèle de base vs modèle fine-tuné :
import { FineTuningOrchestrator, FeedbackCollector } from '@orka-js/finetuning';import { OpenAIAdapter } from '@orka-js/openai'; interface ABTestResult { modelType: 'base' | 'finetuned'; response: string; rating?: number; latency: number;} class ABTestManager { private baseModel: OpenAIAdapter; private fineTunedModel: OpenAIAdapter; private collector: FeedbackCollector; private results: ABTestResult[] = []; constructor(fineTunedModelName: string) { this.baseModel = new OpenAIAdapter({ apiKey: process.env.OPENAI_API_KEY!, model: 'gpt-3.5-turbo' }); this.fineTunedModel = new OpenAIAdapter({ apiKey: process.env.OPENAI_API_KEY!, model: fineTunedModelName }); this.collector = new FeedbackCollector({ storage: 'file', filePath: './ab-test-results.jsonl' }); } async runTest(userInput: string): Promise<void> { // Sélectionner aléatoirement le modèle à utiliser (50/50) const useFineTuned = Math.random() > 0.5; const model = useFineTuned ? this.fineTunedModel: this.baseModel; const modelType = useFineTuned ? 'finetuned' : 'base'; const startTime = Date.now(); const response = await model.generate({ messages: [{ role: 'user', content: userInput }] }); const latency = Date.now() - startTime; const result: ABTestResult = { modelType, response: response.content, latency }; this.results.push(result); // Afficher la réponse à l'utilisateur et collecter le feedback console.log(`Modèle : ${modelType}`); console.log(`Réponse : ${response.content}`); console.log(`Latence : ${latency}ms`); return; } async recordFeedback(index: number, rating: number): Promise<void> { const result = this.results[index]; if (!result) return; result.rating = rating; await this.collector.addFeedback({ input: 'Requête utilisateur', output: result.response, rating, metadata: { modelType: result.modelType, latency: result.latency, timestamp: new Date().toISOString() } }); } async getAnalytics() { const baseResults = this.results.filter(r => r.modelType === 'base' && r.rating); const fineTunedResults = this.results.filter(r => r.modelType === 'finetuned' && r.rating); const avgRatingBase = baseResults.reduce((sum, r) => sum + (r.rating || 0), 0) / baseResults.length; const avgRatingFineTuned = fineTunedResults.reduce((sum, r) => sum + (r.rating || 0), 0) / fineTunedResults.length; const avgLatencyBase = baseResults.reduce((sum, r) => sum + r.latency, 0) / baseResults.length; const avgLatencyFineTuned = fineTunedResults.reduce((sum, r) => sum + r.latency, 0) / fineTunedResults.length; return { base: { count: baseResults.length, avgRating: avgRatingBase.toFixed(2), avgLatency: avgLatencyBase.toFixed(0) + 'ms' }, fineTuned: { count: fineTunedResults.length, avgRating: avgRatingFineTuned.toFixed(2), avgLatency: avgLatencyFineTuned.toFixed(0) + 'ms' }, improvement: { rating: ((avgRatingFineTuned - avgRatingBase) / avgRatingBase * 100).toFixed(1) + '%', latency: ((avgLatencyBase - avgLatencyFineTuned) / avgLatencyBase * 100).toFixed(1) + '%' } }; }} // Utilisationasync function runABTest() { const abTest = new ABTestManager('ft:gpt-3.5-turbo:customer-support-v1'); // Exécuter 100 tests const queries = [ "Comment réinitialiser mon mot de passe ?", "Quels sont vos horaires d'ouverture ?", "Comment annuler mon abonnement ?", // ... plus de requêtes ]; for (let i = 0; i < queries.length; i++) { await abTest.runTest(queries[i]); // Simuler une note utilisateur (en production, obtenir de vrais retours) const rating = Math.floor(Math.random() * 3) + 7; // 7-10 await abTest.recordFeedback(i, rating); } // Obtenir les analytics const analytics = await abTest.getAnalytics(); console.log('Résultats du test A/B:', analytics); // Exemple de sortie : // { // base: { count: 52, avgRating: '7.8', avgLatency: '1250ms' }, // fineTuned: { count: 48, avgRating: '8.9', avgLatency: '980ms' }, // improvement: { rating: '+14.1%', latency: '+21.6%' } // }} runABTest().catch(console.error);Exemple 3 : Boucle d'Apprentissage Continu
Réentraîner automatiquement les modèles selon les nouveaux retours :
import { FeedbackCollector, DatasetValidator, FineTuningOrchestrator } from '@orka-js/finetuning'; class ContinuousLearningPipeline { private collector: FeedbackCollector; private validator: DatasetValidator; private orchestrator: FineTuningOrchestrator; private retrainThreshold = 1000; // Réentraîner après 1000 nouveaux retours private minRating = 8; // Utiliser uniquement les retours de haute qualité constructor() { this.collector = new FeedbackCollector({ storage: 'database', connectionString: process.env.DATABASE_URL! }); this.validator = new DatasetValidator(); this.orchestrator = new FineTuningOrchestrator({ provider: 'openai', apiKey: process.env.OPENAI_API_KEY! }); } async checkAndRetrain(): Promise<void> { const feedbackCount = await this.collector.getCount(); const lastRetrainCount = await this.getLastRetrainCount(); const newFeedbackCount = feedbackCount - lastRetrainCount; console.log(`Nouveaux retours depuis le dernier entraînement : ${newFeedbackCount}`); if (newFeedbackCount >= this.retrainThreshold) { console.log('Seuil atteint. Démarrage du réentraînement...'); await this.retrain(); } } private async retrain(): Promise<void> { // Exporter les retours de haute qualité const dataset = await this.collector.exportDataset({ format: 'openai', minRating: this.minRating, includeCorrections: true, since: await this.getLastRetrainDate() }); console.log(`${dataset.length} nouveaux exemples d'entraînement exportés`); // Valider const validation = await this.validator.validate(dataset, { provider: 'openai', checkDuplicates: true, checkBalance: true }); if (!validation.valid) { console.error('Échec de la validation du dataset:', validation.errors); await this.notifyTeam('Échec de la validation du dataset', validation.errors); return; } // Estimer le coût const costEstimate = await this.orchestrator.estimateCost({ dataset, baseModel: 'gpt-3.5-turbo', epochs: 3 }); console.log(`Coût estimé : ${costEstimate.estimatedCost.toFixed(2)}$`); // Auto-approuver si le coût est en dessous du seuil if (costEstimate.estimatedCost > 100) { await this.notifyTeam('Approbation manuelle nécessaire', { cost: costEstimate.estimatedCost, examples: dataset.length }); return; } // Créer le job const job = await this.orchestrator.createJob({ trainingData: dataset, baseModel: 'gpt-3.5-turbo', suffix: `v${await this.getNextVersion()}`, hyperparameters: { nEpochs: 3, batchSize: 4 } }); console.log(`Job de fine-tuning créé : ${job.id}`); // Sauvegarder les métadonnées du réentraînement await this.saveRetrainMetadata({ jobId: job.id, feedbackCount: await this.collector.getCount(), timestamp: new Date() }); // Surveiller le job await this.monitorAndDeploy(job.id); } private async monitorAndDeploy(jobId: string): Promise<void> { const checkInterval = setInterval(async () => { const jobs = await this.orchestrator.listJobs(); const job = jobs.find(j => j.id === jobId); if (!job) { clearInterval(checkInterval); return; } if (job.status === 'succeeded') { console.log(`Job ${jobId} réussi !`); console.log(`Nouveau modèle : ${job.fineTunedModel}`); clearInterval(checkInterval); // Auto-déployer en staging await this.deployToStaging(job.fineTunedModel!); // Notifier l'équipe await this.notifyTeam('Nouveau modèle déployé en staging', { model: job.fineTunedModel, jobId: job.id }); } else if (job.status === 'failed') { console.error(`Job ${jobId} échoué :`, job.error); clearInterval(checkInterval); await this.notifyTeam('Échec du fine-tuning', { jobId, error: job.error }); } }, 60000); // Vérifier chaque minute } private async deployToStaging(modelName: string): Promise<void> { // Mettre à jour la variable d'environnement ou la config console.log(`Déploiement de ${modelName} en staging...`); // L'implémentation dépend de votre configuration de déploiement } private async notifyTeam(subject: string, data: any): Promise<void> { // Envoyer une notification (Slack, email, etc.) console.log(`[NOTIFICATION] ${subject}`, data); } private async getLastRetrainCount(): Promise<number> { // Obtenir depuis la base de données return 0; } private async getLastRetrainDate(): Promise<Date> { // Obtenir depuis la base de données return new Date(0); } private async getNextVersion(): Promise<number> { // Obtenir depuis la base de données return 1; } private async saveRetrainMetadata(metadata: any): Promise<void> { // Sauvegarder en base de données console.log('Sauvegarde des métadonnées de réentraînement:', metadata); }} // Exécuter comme un cron job (ex: quotidien)async function main() { const pipeline = new ContinuousLearningPipeline(); await pipeline.checkAndRetrain();} // Ou exécuter en continuasync function runContinuously() { const pipeline = new ContinuousLearningPipeline(); setInterval(async () => { await pipeline.checkAndRetrain(); }, 24 * 60 * 60 * 1000); // Vérifier quotidiennement} main().catch(console.error);