cross_val_scoreの値がマイナスなら、最初にscoringの指定を確認します。 neg_mean_squared_errorはMSEの符号を反転したスコアです。一方、R²が負になった場合まで符号を反転すると、評価の意味が変わってしまいます。
この記事は、回帰モデルの交差検証を始め、教材の「負の誤差」に疑問を持った人向けです。Pythonの配列と平均が分かれば、六行の架空データから手計算できます。モデルを改良する前に、何をどの向きで評価しているかを読める状態にしましょう。
公式資料の確認日:2026年10月24日。評価指標とスコアの規約、cross_val_score、root_mean_squared_error、DummyRegressorを参照しています。現行資料と実行確認のscikit-learnは1.9.1、Pythonは3.12.14です。以下の数値は編集部の架空教材の結果で、実案件やスクール受講成果ではありません。
負の評価値は、scoringと良し悪しの向きから読む
negの付いた誤差は、大きいほど良いスコアへ変換されている
MSEは、正解と予測の差を二乗して平均した値です。小さい方が誤差が少なく、0ならその評価データに対して差がありません。一方、scikit-learnのscorerは、大きい値ほど良いという方向へ揃えています。この違いを調整するため、MSEを負にしたスコアが用意されています。
例えばMSEが4と9の二つの候補は、通常の誤差なら4の方が小さい状態です。スコアに直すとマイナス4とマイナス9になり、大きい方のマイナス4が同じ候補を指します。負だから失敗、小さいほど良い、と一つのルールで読むと比較が逆になります。
neg_mean_squared_errorのnegを、予測値の符号と混同しないでください。予測対象が負の値を取るかとは別の話です。予測対象がすべて正でも、このscoringなら0以下のスコアが返ります。まず引数の名前から、返り値の約束を確認しましょう。
学習ノートには「平均スコア」だけでなく、指標名を残します。マイナス4という一つの数字からは、MSEなのか、MAEなのか、RMSEなのかを判断できません。データの単位と分割方法まで添えると、後で別の実験と混同しにくくなります。
MSE・RMSE・MAEは、符号を戻した後も別の指標
negが付いた指標を表示用に戻す操作は、基本的にマイナスを掛けることです。ただし、指標そのものの計算式まで同じになるわけではありません。MSEの符号を戻しただけで、元の単位の誤差として説明しないようにします。
MSEは差を二乗した平均なので、目的変数が分なら単位は分の二乗です。RMSEはMSEの平方根で、元の分の単位になります。MAEは差の絶対値の平均で、これも分の単位ですが、大きな差の影響の受け方はRMSEと異なります。
| scoring | 表示用の変換 | 通常の誤差の向き |
|---|---|---|
| neg_mean_squared_error | MSEは-scores。各回のRMSEはsqrt(-scores) | 小さい方が誤差が少ない |
| neg_root_mean_squared_error | RMSEは-scores。さらに平方根を取らない | 小さい方が誤差が少ない |
| neg_mean_absolute_error | MAEは-scores。平方根を取らない | 小さい方が誤差が少ない |
| r2 | 通常はそのまま読む。負でも符号反転しない | 大きい方が良い。負の値も取り得る |
表中のsqrtは平方根を意味します。neg_root_mean_squared_errorには、指標の計算にすでに平方根が含まれています。名前の一部だけ見て、どのスコアにも同じ処理をコピーしないでください。変換前後の列名を分けると確認しやすくなります。
また、MSEやRMSEの値を、そのまま正解率へ置き換えることはできません。RMSEが2だから二割の誤り、という読み方もしません。対象の単位、値の範囲、用途に照らして判断します。本稿では分の教材として計算の意味だけを確認し、一般的な合格ラインは置きません。
単位の変更を考えると違いを確かめられます。正解と予測の両方を10倍にした場合、差も10倍になるのでMAEとRMSEは10倍、MSEは100倍になります。指標の数字が大きくなっただけでモデルが悪化したとは限らず、対象の単位が変わった可能性もあります。分を秒へ変えた結果を比較する場合も、同じ単位に戻す作業が先です。
評価表の見出しに「誤差」だけを置くと、MSEをRMSEと並べて小さい方を選ぶ誤りが起きやすくなります。「MSE・分の二乗」「RMSE・分」「平均neg MSE」のように列名へ書き分けてください。元のscoring文字列を別に残しておくと、表示時に符号を二回反転していないかも点検できます。
六行の教材で、各回のスコアとMSEを対応させる
ここでは予測対象を0、0、0、0、4、4の六行とし、どの入力にも0を返す比較用モデルを使います。三分割し、各回二行を評価に使います。モデルの性能を高く見せる練習ではなく、値の変換を手計算できるようにした教材です。
最初の二回は正解も予測も0なので、MSEは0です。最後の回では正解が4、予測が0で、二件とも差の二乗が16になります。したがって通常のMSEは0、0、16、neg MSEのスコアは0、0、マイナス16です。
import numpy as np
from sklearn.dummy import DummyRegressor
from sklearn.model_selection import KFold, cross_val_score
X = np.arange(6).reshape(-1, 1)
y = np.array([0, 0, 0, 0, 4, 4], dtype=float)
cv = KFold(n_splits=3, shuffle=False)
model = DummyRegressor(strategy="constant", constant=0)
scores = cross_val_score(
model, X, y, cv=cv,
scoring="neg_mean_squared_error", error_score="raise",
)
mse_by_fold = -scores
rmse_by_fold = np.sqrt(mse_by_fold)
np.testing.assert_allclose(scores, [0, 0, -16])
np.testing.assert_allclose(mse_by_fold, [0, 0, 16])
np.testing.assert_allclose(rmse_by_fold, [0, 0, 4])
print("score:", scores)
print("MSE:", mse_by_fold)
print("RMSE:", rmse_by_fold)
結果にマイナス0が表示されても、今回の0と異なる大きさの誤差を意味しません。浮動小数点の表示として符号が見える場合があります。一方、マイナス16は符号を戻して16となる損失です。表示を整える前に、元の配列をそのまま見て対応を確認します。
入力Xの値を今回のモデルは予測に使いません。定数を返すDummyRegressorを選んだのは、学習済みの複雑な予測値を追うより、正解と0の差を確認する方が仕組みを理解しやすいためです。現実の予測モデルをこの例だけで選ぶことはできません。
六件をR1からR6と呼ぶと、一回目の評価対象はR1・R2、二回目はR3・R4、三回目はR5・R6です。それ以外の四件を各回の訓練側に使います。今回の定数0は訓練データが変わっても同じ値を返すため、評価対象と誤差の対応へ集中できます。一般のモデルなら訓練対象が変わることで予測も変わるので、全行を一回だけ予測した結果と無条件に同一視しないでください。
コードを教材へ合わせるためにデータを並べ替えた場合も、その行順を記録します。実データの性質を無視した分割が適切になるわけではありませんが、同じ入力で同じ計算をたどれることは、学習段階の切り分けに役立ちます。期待値が合わないときは、モデルの調整より先に、六行の順番と三つの評価組を確認しましょう。
負のR²をプラスへ直すと、評価を読み違える
R²はneg MSEとは別の指標です。名前にnegが付いていないR²が負になることもあります。これは、符号が反転した通常の誤差だから元へ戻す、という状態ではありません。R²の式と評価データの分布から解釈する必要があります。
負の値を取ることや定数の目的変数を扱う場合の仕様は、公式のr2_scoreも確認してください。ここでは正解の値がすべて同じではない、三件の教材へ範囲を絞ります。
次の三件では正解が1、2、3、予測がすべて0です。差の二乗和は14で、正解の平均2を基準にした二乗和は2です。R²は1から14割る2を引いたマイナス6になります。これをプラス6にして「良くなった」と表示してはいけません。
from sklearn.metrics import r2_score
r2 = r2_score([1, 2, 3], [0, 0, 0])
assert np.isclose(r2, -6.0)
print("R2:", r2) # -6.0。符号はそのまま残す
交差検証のscoringを省略した場合、推定器の既定の評価方法が使われます。モデルを変えたときに、以前と同じ誤差を見ているとは限りません。回帰の教材で見かけるscoreはR²であることもあるので、引数とモデルの評価方法の両方を確認します。
負の値をすべて絶対値に変える処理は、こうした違いを隠します。R²のマイナス6とプラス6を同じ値へしてしまい、比較に必要な情報が失われます。表示処理を共通化する場合も、指標名ごとに変換の有無を決めてください。
モデル比較では、負のスコアを小さい順にしない
通常のMSEを小さい順に並べることと、neg MSEのスコアを小さい順に並べることは逆の選択です。スコアを使うなら大きい方、表示用のMSEを使うなら小さい方と、比較する列を決めます。変換前後を一つの列へ混在させないことも大切です。
教材の定数0モデルと、定数2モデルを、同じ六行・同じ三分割で比べます。定数2のMSEは各回4、4、4なので平均4です。定数0の平均MSEは約5.333です。この条件では、平均neg MSEがマイナス4の定数2が、マイナス5.333の定数0より大きくなります。
candidate_scores = {}
for constant in [0, 2]:
candidate_scores[constant] = cross_val_score(
DummyRegressor(strategy="constant", constant=constant),
X, y, cv=cv, scoring="neg_mean_squared_error", error_score="raise",
)
means = {key: float(value.mean()) for key, value in candidate_scores.items()}
best_by_score = max(means, key=means.get)
losses = {key: -value for key, value in means.items()}
best_by_mse = min(losses, key=losses.get)
assert best_by_score == best_by_mse == 2
print("平均neg MSE:", means)
print("平均MSE:", losses)
これは教材内の候補比較であり、定数2が他のデータでも優れることを示しません。分割を変えた結果と混ぜたり、候補ごとに評価対象を減らしたりすると比較条件がずれます。モデル名とスコアだけでなく、分割を揃えたことも記録しましょう。
小さな差だけを見て結論を急がず、各回のばらつきや評価対象も確認します。平均は要約なので、最後の一回だけ誤差が大きい定数0の特徴を隠す場合があります。元の配列を残しておけば、候補の違いをどの回が作っているか説明できます。
候補を何度も比べて選ぶために使った交差検証の結果と、最後に独立したデータで確認する結果も分けて記録します。選択に使った値だけを「未知のすべてのデータに対する性能」と呼ぶのは無理があります。今回の例なら「六件、三分割、二候補という条件内の比較」と範囲を添えるだけでも、結論を広げ過ぎずに済みます。
RMSEの平均と検証の条件を、計算の順番まで揃える
各回のRMSEの平均と、平均MSEの平方根は違う
六行の例では、各回のRMSEは0、0、4です。この三つを平均すると約1.333分です。一方、MSEの0、0、16を平均してから平方根を取ると約2.309分になります。どちらも同じ素材から計算していますが、平方根を取る順番が異なります。
平均と平方根は順番を入れ替えて同じ値になる計算ではありません。「交差検証のRMSE」とだけ書くと、どちらを報告しているか分かりません。各回のRMSEを平均した値なのか、誤差の二乗をまとめて平均した平方根なのかを明記します。
rmse_scores = cross_val_score(
model, X, y, cv=cv,
scoring="neg_root_mean_squared_error", error_score="raise",
)
mean_fold_rmse = (-rmse_scores).mean()
sqrt_mean_fold_mse = np.sqrt((-scores).mean())
assert np.isclose(mean_fold_rmse, 4 / 3)
assert np.isclose(sqrt_mean_fold_mse, np.sqrt(16 / 3))
assert not np.isclose(mean_fold_rmse, sqrt_mean_fold_mse)
print(round(mean_fold_rmse, 6)) # 1.333333
print(round(sqrt_mean_fold_mse, 6)) # 2.309401
今回の三分割では各回の評価件数が二件で等しいため、MSEの単純平均は六件をまとめた二乗誤差の平均とも一致します。ただし、各回の件数が違う場合まで同じとは限りません。次の節では件数の違いを別に確認します。
また、複数の目的変数やサンプル重みを使った場合は、どの段階で平均や重み付けを行うかが増えます。本稿の比較は一つの目的変数、重みなしの教材です。条件を増やしたら、単純な式をそのまま流用せず、使った指標の仕様と結果を確認してください。
評価件数が違う回をまとめるなら、重みの意味を決める
例えば一つの回に四件、別の回に二件あり、それぞれのMSEが0と16だったとします。二回を同じ重みで平均すると8です。全六件の二乗誤差としてまとめるなら、四件分の0と二件分の16から、32割る6で約5.333になります。
どちらかを無条件に正解と決めるより、何を同じ重さで扱うかを決めます。「各分割の結果」を同じ重みで要約する場合と、「各データ行」を同じ重みでまとめる場合は、集計対象が異なります。報告の目的に合う方を明示してください。
fold_mse = np.array([0., 16.])
fold_sizes = np.array([4, 2])
equal_fold_mean = fold_mse.mean()
pooled_mse = np.average(fold_mse, weights=fold_sizes)
assert np.isclose(equal_fold_mean, 8.)
assert np.isclose(pooled_mse, 32 / 6)
print("各回を同じ重み:", equal_fold_mean)
print("各行を同じ重み:", pooled_mse)
この二回の数字は集計方法の説明用で、上のKFold実行結果ではありません。実データへ使うときは、実際の各回の件数と二乗誤差を取得して計算します。架空の件数を足して平均を調整したり、一部の回だけ除外して見栄えを整えたりしないでください。
評価用の行が重複して登場する分割や、各行が一度ずつ評価されない方式もあります。その場合、単に回の件数を掛けるだけで「全データの一回の評価」と同じにはなりません。最初は各回の対象IDと件数を控え、使った分割方法の意味を確認するのが近道です。
分割・前処理・指標名を一緒に記録する
同じ指標でも分割方法が変われば、評価対象が変わります。今回のKFoldは三分割、shuffleなしです。教材の行順を固定したからこそ、最初の二回が0、最後が16という結果を手計算できました。実データで常にこの分け方が適切という意味ではありません。
時間順の予測、同じ人物や装置から何度も採った記録などでは、独立した行として無作為に分けてよいかを考えます。未来の情報が訓練に混ざる、同じ対象が両側に出る、といった問題は、スコアの符号を直しても解決しません。評価設計の問題として分けて確認します。
前処理の統計量も、各回の訓練データで決める必要があります。全体の平均で標準化してから交差検証を呼ぶと、評価側の分布を使ってしまいます。StandardScalerと分割の順序を確認し、必要な前処理とモデルをPipelineへまとめる方法を検討してください。
カテゴリ列を含む場合は、符号の確認と分けて、評価側に訓練時にはなかった値が出たときの処理も点検します。OneHotEncoderの未知カテゴリと全ゼロの確認で、前処理の設定と評価結果を一緒に読めるようにしておきましょう。
分類のstratifyエラーで止まっている場合は、今回の符号の話へ進む前にクラスごとの件数と分割後の確認で分割自体を点検します。評価関数が実行できたことと、比較が目的に合っていることも別の確認です。
記録する項目は、データの識別名、目的変数の単位、モデル、scoring、分割器と設定、各回の件数、各回の生スコア、表示用変換、集計式です。長い実験でも、この表があれば「負の数字をどこで戻したか」「何の平均か」を後から追えます。
一度決めた比較条件を変えるときは、以前の結果を上書きせず、変更した項目を一つ記録します。例えばscoringだけをneg MSEからneg RMSEへ変えたなら、平均の取り方によって候補の見え方が変わる可能性があります。値の絶対的な大小を別の指標と比べるのではなく、各指標の意味と選ぶ目的に沿って読んでください。条件のメモが残っていれば、変化をコードの不具合と決め付けずに説明できます。
NaNや異常な値は、絶対値にせず原因を調べる
スコアがNaNなら、negの符号が問題とは限りません。学習や評価の途中で例外が起きた場合、cross_val_scoreのerror_score設定によって欠損のスコアが返ることがあります。警告を読まずに平均だけ出すと、何回を評価できたのかが分かりにくくなります。
小さな再現例で原因を調べる段階では、教材のようにerror_score="raise"を指定し、例外をその場で確認する方法があります。何が起きたかを把握してから、実運用で停止、保留、記録のどれを選ぶかを決めます。エラーを隠すためだけの設定変更は避けましょう。
入力に欠損が残っていないか、目的変数とXの行数や順番が合っているか、選んだscoringが推定器に適しているかを確認します。数値へ変換した段階で問題が出ているなら、pandasのmap後の欠損を切り分ける練習など、前段の処理へ戻ります。
np.nanmeanへ変えれば見える数字が出ることもありますが、それだけで評価が完了したとは言えません。失敗した回を除いた平均なら、その事実と回数を表示する必要があります。モデル間で欠損の回が違えば、同じ条件の比較にもならなくなります。
相談するときは、ライブラリの版、最小のXとy、モデル、scoring、cv、警告・例外の全文、期待した計算をまとめます。機密データを貼らず、六行程度の架空例へ置き換えても問題が再現するか試してください。受講前に準備したいPython・数学の確認にも役立つ説明練習です。
よくある質問と、評価結果を人に伝える書き方
質問と回答
Q. cross_val_scoreがマイナスなら学習に失敗していますか?
負の値だけでは判断できません。neg_mean_squared_errorなどは損失の符号を反転したスコアです。まずscoringを確認します。NaNや例外がある場合は別に調べてください。
Q. neg_mean_squared_errorへマイナスを掛ければRMSEですか?
符号を戻した値はMSEです。各回のRMSEにするなら、さらに平方根を取ります。neg_root_mean_squared_errorなら符号を戻すだけでRMSEになります。
Q. 負のスコアは小さい方が良いですか?
scikit-learnのscorerは大きい方が良いという方向です。neg MSEならマイナス4はマイナス9より良いスコアです。通常のMSEに戻した後は、小さい方が誤差が少ないと読みます。
Q. R²が負なら符号を戻してよいですか?
R²へ同じ変換はしません。負のR²にも指標としての意味があります。neg付きの損失スコアと区別し、そのままの値で解釈します。
Q. 各回のRMSEの平均と、平均MSEの平方根が違います。
平方根を取る順番が違うためです。教材では前者が約1.333分、後者が約2.309分になります。報告時にはどちらを計算したか明記します。
Q. scoringを省略すると何が返りますか?
推定器の既定の評価方法が使われます。いつもMSEが返るわけではありません。モデルのscoreの仕様を確認し、比較の練習ではscoringを明示すると混同を減らせます。
教材の結果を伝えるなら、「定数0モデル、KFold三分割、各回二件、neg MSEは0・0・マイナス16。通常のMSEは0・0・16、各回のRMSE平均は約1.333分」と書けます。指標名と集計式を省かないことが、次のモデルを比較する土台になります。数値を大きく見せるより、同じ条件で読み直せる記録を残しましょう。
評価指標だけでなく、Pythonと機械学習の学習順序から相談したい場合は、Python WinnerのAI・機械学習系の講座も比較できます。公式の講座範囲と、この教材のCV・指標選択を質問できる範囲は分けて確認してください。学習前提、教材の版、レッスン内の質問方法、総額を受講相談で照合し、今回の疑問だけなら公式資料と六件の教材で解決できるかを先に試すと判断しやすくなります。(公式情報確認日:2026年10月24日、Python Winner公式)。
広告: 【PythonWinner】![]()