実験機: Core i7-14700F、RTX 5070 Ti 16 GB、RAM 32 GB
環境: Windows 11、WSL 2、Ubuntu 26.04 LTS、Python 3.12.14、PyTorch 2.12.1+cu130(予測)/2.11.0+cu130(異常検知)
NVIDIAのNV-TesseractをWSL 2へ導入し、RTX 5070 Tiで時系列予測と異常検知を実行できました。
先に紛らわしい点を片付けると、これは画像から文字を読むOCRのTesseractではありません。数値が時間順に並ぶ「時系列データ」を扱うNVIDIAのオープンソースライブラリです。予測にはTransformer系の基盤モデル、異常検知には拡散モデルが入っています。
今回の主な結果は次のとおりです。
| 確認項目 | 結果 |
|---|---|
| WSL 2からCUDAを認識 | 成功 |
| ETThサンプルの100点予測 | 成功 |
| 予測1回目(モデル読込込み、重み取得済み) | 6.881秒 |
| 同一プロセスの予測2回目 | 0.080秒 |
| 予測時のPyTorch peak allocated | 2,824.5 MiB |
| AD Diffusionの500点異常検知 | 成功 |
| 異常検知のGPU使用率(200 ms間隔の最大値) | 81% |
異常検知のGPUメモリ(nvidia-smi最大値) |
1,483 MiB |
動きました。
ただし、異常検知の公式サンプルはそのまま2回実行すると検出数が変わりました。PyTorch側の乱数も固定すると2回の出力は完全一致します。「GPUで走った」と「再現可能な実験になった」は別です。ここが地味ですが重要でした。

NV-Tesseractは何をするものか
NVIDIA公式リポジトリには、次の2モジュールが含まれています。
forecasting/: 過去の時系列から未来の値を予測するad_diffusion/: 複数センサーなどの時系列から異常候補を検出する
予測とは、たとえば直前512時間の負荷から、その後72時間や100時間の値を推定する処理です。異常検知は、入力をモデルで再構成し、元データとの差が大きい点を異常候補として扱います。今回の異常度はMAE(Mean Absolute Error、平均絶対誤差)で表されます。
公式要件はPython 3.12以上、PyTorch 2.7以上、pandas、NumPyです。GPUが推奨されていますが、CUDAが無い場合はCPUへフォールバックする設計です。今回はGPUで動くことを確かめるため、実行前にCUDAの認識まで確認しました。
必要なものと空き容量
必須
- Windows 11とWSL 2
- WSL側のUbuntu
- WSLから利用できるNVIDIA GPUドライバ
- Git
- インターネット接続
- 15 GB以上の空き容量(予測モジュールだけの目安)
あると確認しやすいもの
nvidia-smi- RTX系GPU
- 16 GB程度のGPUメモリ
16 GBのVRAMを使い切ったわけではありません。予測のPyTorch peak allocatedは約2.76 GiBでした。ただし、この1条件だけから「すべての8 GB GPUで必ず動く」とまでは言えません。入力チャンネル数、batch size、説明機能、別checkpointで必要量は変わります。
ディスク側はやや重めです。このPCでは予測用.venvが約5.0 GB、NVIDIAの予測checkpointが約1.40 GB、MOMENT backboneが約1.39 GBでした。uvのdownload cacheも残るため、ぎりぎりの空き容量で始めない方が安全です。
管理者権限とWindows再起動は、すでにWSLとGPUドライバが動いていれば不要でした。WSLからCUDAを使える状態がまだ無い場合は、先に「Windows 11+WSL 2でPyTorchからRTX 5070 Tiを使う」の確認手順を通すと切り分けやすくなります。
WSL 2からGPUを確認する
まずUbuntuを開き、GPUとPythonを確認します。
/usr/lib/wsl/lib/nvidia-smi
python3 --version
git --version
このPCでは次を確認できました。
NVIDIA GeForce RTX 5070 Ti
Driver Version: 595.71
GPU Memory: 16303 MiB
Python 3.14.4
git version 2.53.0
システムPythonは3.14でしたが、NV-Tesseract用には3.12の隔離環境を作ります。プロジェクトごとにPythonとpackageを分けると、別のPyTorch環境を壊しにくくなります。
uvとNV-Tesseractを導入する
このPCにはuvが無かったため、小さなbootstrap用venvへ入れました。
python3 -m venv ~/.venvs/nv-tesseract-bootstrap
~/.venvs/nv-tesseract-bootstrap/bin/python -m pip install --upgrade pip uv
~/.venvs/nv-tesseract-bootstrap/bin/uv --version
確認できたversionはuv 0.12.8です。続いて公式repositoryを取得し、予測用環境を同期します。
git clone https://github.com/NVIDIA/NV-Tesseract.git
cd NV-Tesseract/forecasting
~/.venvs/nv-tesseract-bootstrap/bin/uv sync --python 3.12
初回はPyTorchとCUDA関連packageをまとめてdownloadします。このPCでは91 packageが入りました。
repositoryを/mnt/c配下へ置き、cacheをWSL filesystemへ置いたところ、uvはhardlinkを作れずcopyへ切り替わりました。処理自体は成功しますが、展開に約2分45秒かかりました。次のwarningは失敗ではありません。
warning: Failed to hardlink files; falling back to full copy.
If the cache and target directories are on different filesystems,
hardlinking may not be supported.
Windowsから原稿やCSVを直接開きたかったので今回は/mnt/cを使いました。速度を優先する場合は、repositoryとcacheを同じWSL filesystem側へ置く構成も検討できます。
PyTorchからRTX 5070 Tiが見えているか
予測を始める前に、作成されたPythonでCUDAを確認します。
.venv/bin/python -c "import torch; print(torch.__version__); print(torch.version.cuda); print(torch.cuda.is_available()); print(torch.cuda.get_device_name(0)); print(torch.cuda.get_device_capability(0))"
実際の出力です。
2.12.1+cu130
13.0
True
NVIDIA GeForce RTX 5070 Ti
(12, 0)
torch.cuda.is_available()がTrueで、GPU名も一致しています。(12, 0)はcompute capabilityです。ここまで通れば、少なくとも「CPU版PyTorchを入れてしまった」「WSLからGPUが見えない」という段階は抜けています。
ETThの512点から100点を予測する
予測にはrepository同梱のETTh_single_feature.csvを使いました。ETThは電力変圧器温度の時系列で、今回はLULL列の最後512点を入力し、その先100点を出します。
固定した条件は次のとおりです。
| 項目 | 設定 |
|---|---|
| 入力 | 公式ETTh single-feature sample |
| target | LULL |
入力長 seq_len |
512 |
出力長 forecast_horizon |
100 |
| model native horizon | 72 |
| batch size | 1 |
| seed | 42 |
| cross-channel | 無効 |
最小構成は次のようになります。model_horizonは公開checkpointに合わせて72のままにします。
from pathlib import Path
import pandas as pd
from sdk.forecasting import ForecastingConfig, perform_forecasting
csv_path = Path("sdk/tests/datasets/ETTh_single_feature.csv")
df = pd.read_csv(csv_path).tail(512)
config = ForecastingConfig(
timestamp_column="timestamp",
target_column="LULL",
seq_len=512,
forecast_horizon=100,
model_horizon=72,
batch_size=1,
num_workers=0,
seed=42,
use_cross_channel=False,
)
forecast = perform_forecasting(df=df, config=config)
forecast.to_csv("forecast.csv", index=False)
print(forecast.head(10))
初回はHugging Faceから重みを自動取得します。予測checkpointは約1.40 GB、MOMENT backboneは約1.39 GBでした。認証なしで取得できましたが、未認証requestのrate limit warningは表示されました。
出力はtimestampとLULL_forecastを持つ100行のDataFrameです。先頭10点は次の値でした。
1.2353706, 1.3113322, 1.4206676, 1.4212670, 1.3020000,
1.4943643, 1.4871302, 1.5049992, 1.4743977, 1.4228201

この図は「未来を正しく当てた」ことの証明ではありません。今回は実行可否と出力形状を確認するprobeであり、予測区間の正解データとMAEを比較していないためです。精度評価では入力末尾をさらに手前へずらし、隠した100点と予測を比較する必要があります。
100点予測で起きたcheckpoint形状エラー
最初はforecast_horizon=100に合わせ、model_horizonも100へ変更しました。するとcheckpoint読込時に止まりました。
RuntimeError: Error(s) in loading state_dict for BackbonePipeline:
size mismatch for head.linear.weight:
checkpoint torch.Size([72, 65536])
current model torch.Size([100, 65536])
公開checkpointの予測headは72出力です。一方で100に作り替えたmodelは100出力を要求するため、重みの形が合いません。
修正はmodel_horizon=72へ戻し、forecast_horizon=100だけを指定することでした。SDKはnative horizonを超えた残り28点を自己回帰で延長します。NVIDIAのmodel cardにも、native capabilityを超えるhorizonはautoregressive extensionに対応すると記載されています。
正直、100点欲しいから両方100にする、という設定はそれっぽく見えます。checkpointの出力層まで変わるのが厄介です。
モデル読込込みとwarm runを分けて測る
同じ入力を同一process内で2回実行しました。どちらも重みfileはdownload済みです。
| run | 含まれる処理 | 時間 | PyTorch peak allocated | peak reserved |
|---|---|---|---|---|
| 1 | checkpoint読込+model構築+推論 | 6.881秒 | 2,824.5 MiB | 2,828 MiB |
| 2 | cache済みmodel+推論 | 0.080秒 | 2,060.9 MiB | 2,082 MiB |
1回目を「純粋な推論速度」と呼ぶのは不正確です。modelの読込とGPUへの配置が含まれています。2回目はSDK内のmodel cacheを再利用しており、100点の出力は1回目と一致しました。
また、初回weight downloadはこの表に含めていません。回線速度で大きく変わるため、download、model load、warm inferenceは分けて扱う方が結果を読みやすくなります。
AD Diffusionで500点の異常検知を動かす
異常検知側は別の依存関係を持つため、ad_diffusionにも独立した環境を作りました。
cd ../ad_diffusion
~/.venvs/nv-tesseract-bootstrap/bin/uv sync --python 3.12
.venv/bin/python examples/quick_example.py --download-weights
.venv/bin/python examples/quick_example.py
異常検知checkpointのfinal_model.pthは約29.6 MBです。公式quick exampleはseed 42で500点・3 sensorの合成時系列を作り、spike、dip、level shift、noiseを混ぜます。推論はDPM-Solver 20 step、nsample=15でした。
最初の実行は約13.58秒で推論し、21点を異常として検出しました。precision 1.000、recall 0.171、F1 0.292です。
precisionが1.000なので、検出した点にfalse positiveはありません。一方、recall 0.171なので、正解labelの多くを拾えていません。少数の強い異常だけを選んだ状態です。公式sampleが完走したことと、運用に十分な検出性能であることは分けて考える必要があります。
同じ公式サンプルなのに検出数が変わる
GPU使用量を200 ms間隔で記録しながら公式exampleを2回実行すると、結果は次のように変わりました。
| run | 検出数 | TP | FP | FN | precision | recall | F1 | total elapsed |
|---|---|---|---|---|---|---|---|---|
| 1 | 18 | 18 | 0 | 105 | 1.000 | 0.146 | 0.255 | 27.575秒 |
| 2 | 24 | 24 | 0 | 99 | 1.000 | 0.195 | 0.327 | 28.105秒 |
入力データは同じです。公式exampleは合成データ生成用のnp.random.seed(42)を設定していますが、拡散modelが使うPyTorchの乱数までは固定していません。
そこで、各run前に次を追加しました。
import numpy as np
import torch
np.random.seed(42)
torch.manual_seed(42)
torch.cuda.manual_seed_all(42)
seed固定後は2回とも23点を検出し、Anomaly列は完全一致、MAEの最大絶対差も0でした。実行時間は14.082秒と12.792秒です。

seed固定後のconfusion matrixはTP 23、FP 0、FN 100、TN 377でした。precision 1.000、recall 0.187、F1 0.315です。
ここから分かるのは、seedを固定すれば同じ環境・同じ入力で結果を再現できることです。検出精度が高いという意味ではありません。threshold strategy、nsample、前処理、実データへのfine-tuningは別途評価が必要です。
GPUが本当に使われたか
AD Diffusion実行中にnvidia-smiを200 ms間隔で取得しました。
| 指標 | 観測した最大値 |
|---|---|
| GPU utilization | 81% |
| GPU memory used | 1,483 MiB |
| power draw | 219.3 W |
torch.cuda.is_available()だけでなく、推論中にGPU使用率と消費電力が上がったため、実計算がGPUへ渡ったことを確認できます。ただし200 ms間隔のsampleなので、sample間にある瞬間的なpeakを取り逃す可能性があります。
予測側の2,824.5 MiBはPyTorch allocatorがprocess内で記録したpeak allocated、異常検知側の1,483 MiBはnvidia-smiが見たdevice全体の使用量です。測り方が違うため、両者をそのままmodel間のVRAM比較には使えません。
よく止まりそうな箇所
torch.cuda.is_available()がFalse
NV-Tesseractより手前の問題です。Windows側driver、WSL update、CUDA版PyTorchの順に確認します。WSL内へ通常のLinux用NVIDIA driverを重ねて入れる前に、Windows側driverがWSLへ公開されているかを確認した方が安全です。
100出力checkpointのsize mismatch
forecast_horizon=100でもmodel_horizonは公開checkpointの72へ合わせます。100へ変えるのは出力要求側だけです。
Hugging Faceの401/403
今回は認証なしでdownloadできました。401や403が出る場合は、model pageの利用条件、Hugging Face側のlicense acceptance、token設定を公式READMEに沿って確認します。tokenは記事やlogへ貼らないでください。
diskが急に減る
予測environment、CUDA package、NVIDIA checkpoint、MOMENT backbone、download cacheが別々に存在します。du -sh .venvとcache directoryを確認します。削除前に、どのenvironmentが参照しているかを確かめてください。
実行ごとに異常数が変わる
合成data用のNumPy seedだけでは足りません。PyTorchとCUDA側のseedも固定します。それでもGPU演算によっては完全決定的にならないoperatorがあるため、出力差を測って確認するのが確実です。今回はMAE最大絶対差0まで確認しました。
この結果から判断できること
WSL 2、RTX 5070 Ti、CUDA 13.0系PyTorchの組み合わせで、NV-Tesseractの時系列予測とAD Diffusionは動作しました。予測は公式ETTh sampleから100点を返し、異常検知は公式の500点sampleをGPUで処理できています。
導入上の注意は、VRAMより先にdownload容量とPython環境の分離です。予測は公開weightだけで約2.8 GBあり、CUDA dependencyを含むvenvも約5 GBになりました。一方、今回の予測時PyTorch peak allocatedは約2.76 GiBでした。
また、公式quick exampleの数値をbenchmark表へそのまま転記するのは危険です。異常検知はPyTorch seedを固定しないと検出数が変わり、precision 1.0でもrecallは0.2未満でした。少なくともseed、入力、sampling条件、threshold、計測方法を固定してから比較する必要があります。
この確認は「このPCでrepository同梱sampleが動く」ことを示しています。実センサーの異常を十分に検出できること、別GPUで同じ速度になること、CPU fallbackが実用速度になることまでは証明していません。
再現に使ったartifact
検証scriptでは、次を生dataとして保存しました。
- 予測100点のCSVを2 run分
- Python、PyTorch、CUDA、GPU、commit hash、条件を含むJSON
- GPU memory、utilization、powerの200 ms sample
- AD Diffusionの結果CSVとground truth label
- seed固定前後のconfusion matrix
- console logと未丸めのelapsed time
repositoryはcommit b68ea6f946b9b299d48f91673fd722366c150e99を使用しました。NV-Tesseractは更新が続いているため、結果が変わった場合はcommit hashとlock fileから先に確認すると追いやすくなります。
参考資料
- NVIDIA/NV-Tesseract(GitHub)
- NV-Tesseract Forecasting model card(Hugging Face)
- NV-Tesseract AD Diffusion model card(Hugging Face)
- CUDA GPU Compute Capability(NVIDIA Developer)
関連するGPU測定方法は、batch sizeとthroughput・VRAM・CUDA OOMの実測でも条件を分けて整理しています。
