Files
Aletheia/tools/tts/train_piper_windows.ps1

111 lines
3.5 KiB
PowerShell

[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string] $DatasetDir,
[Parameter(Mandatory = $true)]
[string] $OutputDir,
[Parameter(Mandatory = $true)]
[string] $PythonExe,
[string] $CacheDir,
[string] $CheckpointPath,
[int] $BatchSize = 4,
[int] $NumWorkers = 2,
[int] $MaxEpochs = 2000,
[switch] $SmokeTest
)
$ErrorActionPreference = 'Stop'
$dataset = [IO.Path]::GetFullPath($DatasetDir)
$output = [IO.Path]::GetFullPath($OutputDir)
$python = [IO.Path]::GetFullPath($PythonExe)
$metadata = Join-Path $dataset 'metadata.csv'
$audioDir = Join-Path $dataset 'wav'
$configPath = Join-Path $output 'aletheia_ru.onnx.json'
$cacheDir = if ($CacheDir) {
[IO.Path]::GetFullPath($CacheDir)
} else {
Join-Path $output 'cache'
}
$checkpointDir = Join-Path $output 'checkpoints'
if (-not (Test-Path -LiteralPath $python -PathType Leaf)) {
throw "Python executable not found: $python"
}
if (-not (Test-Path -LiteralPath $metadata -PathType Leaf)) {
throw "Piper metadata not found: $metadata"
}
if (-not (Test-Path -LiteralPath $audioDir -PathType Container)) {
throw "Piper audio directory not found: $audioDir"
}
New-Item -ItemType Directory -Force -Path $output, $cacheDir, $checkpointDir | Out-Null
$fitArgs = @(
'-m', 'piper.train', 'fit',
'--data.voice_name', 'aletheia_ru',
'--data.csv_path', $metadata,
'--data.audio_dir', $audioDir,
'--data.espeak_voice', 'ru',
'--data.cache_dir', $cacheDir,
'--data.config_path', $configPath,
'--data.batch_size', $BatchSize,
'--data.num_workers', $NumWorkers,
'--model.sample_rate', '22050',
'--trainer.accelerator', 'gpu',
'--trainer.devices', '1',
'--trainer.precision', '16-mixed',
'--trainer.max_epochs', $MaxEpochs,
'--trainer.default_root_dir', $checkpointDir
)
if ($SmokeTest) {
$fitArgs += @('--trainer.fast_dev_run', 'true', '--trainer.num_sanity_val_steps', '0')
}
if ($CheckpointPath) {
$checkpointToResume = [IO.Path]::GetFullPath($CheckpointPath)
if (-not (Test-Path -LiteralPath $checkpointToResume -PathType Leaf)) {
throw "Resume checkpoint not found: $checkpointToResume"
}
$fitArgs += @('--ckpt_path', $checkpointToResume)
}
& $python @fitArgs
if ($LASTEXITCODE -ne 0) {
throw "Piper training exited with code $LASTEXITCODE"
}
if ($SmokeTest) {
Write-Output 'PIPER_CUDA_SMOKE_TEST_OK'
exit 0
}
$checkpoint = Get-ChildItem -LiteralPath $checkpointDir -Filter '*.ckpt' -File -Recurse |
Sort-Object LastWriteTimeUtc -Descending |
Select-Object -First 1
if (-not $checkpoint) {
throw "Training finished without a checkpoint under $checkpointDir"
}
$onnxPath = Join-Path $output 'aletheia_ru.onnx'
& $python -m piper.train.export_onnx --checkpoint $checkpoint.FullName --output-file $onnxPath
if ($LASTEXITCODE -ne 0) {
throw "Piper ONNX export exited with code $LASTEXITCODE"
}
$artifacts = [ordered]@{}
foreach ($artifactPath in @($onnxPath, $configPath)) {
if (-not (Test-Path -LiteralPath $artifactPath -PathType Leaf)) {
throw "Expected artifact not found: $artifactPath"
}
$item = Get-Item -LiteralPath $artifactPath
$artifacts[$item.Name] = [ordered]@{
bytes = $item.Length
sha256 = (Get-FileHash -LiteralPath $item.FullName -Algorithm SHA256).Hash.ToLowerInvariant()
}
}
$manifestPath = Join-Path $output 'artifacts.json'
$artifacts | ConvertTo-Json -Depth 4 | Set-Content -LiteralPath $manifestPath -Encoding utf8
$artifacts | ConvertTo-Json -Depth 4