GradDiff は、保持対象の損失を減らし、忘却対象の損失を増やす方向へモデルを更新する手法です。機械学習の忘却(Machine Unlearning)では、対象データを除いて学習し直したモデルを比較基準にできます。TOFU は、忘却対象だけでなく保持対象などを組み合わせて評価する必要性を示しています[1]。
ここでは、線形回帰で GradDiff の目的関数を直接解き、忘却対象の損失が増えても、再学習モデルとの出力差は広がり得ることを確認します。比較するのは、忘却対象の損失、忘却対象での出力差、保持対象での出力差の 3 指標です。二乗損失と L2 正則化を使った定式化は、線形回帰における忘却の分析に基づきます[2]。
固定したデータでは、忘却係数を 0 から 1 へ変えると、忘却対象の平均二乗損失の半分は 7.640496 から 13.749256 に増加します。同時に、再学習モデルとの出力差は忘却対象で 1.334812、保持対象で 1.167717 になります。以下でこの値を再現します。
1. GradDiff の評価対象
全学習データを、保持対象 $(X_r,y_r)$ と忘却対象 $(X_f,y_f)$ に分けます。全データで学習した元モデルを $\theta_{\mathrm{all}}$、保持対象だけで再学習した基準モデルを $\theta_r$、GradDiff の係数 $\rho$ で求めたモデルを $\theta_\rho$ とします。
忘却対象について、1 件当たりの二乗損失の半分を次のように定義します。$m$ は忘却対象の件数です。
L_f(\theta_\rho)=\frac{1}{2m}\|X_f\theta_\rho-y_f\|_2^2
再学習モデルとの出力差は、忘却対象と保持対象で別々に測ります。
\mathrm{RMS}_f=\sqrt{\frac{1}{m}\|X_f(\theta_\rho-\theta_r)\|_2^2}
\mathrm{RMS}_r=\sqrt{\frac{1}{n-m}\|X_r(\theta_\rho-\theta_r)\|_2^2}
これらの RMS は、予測値と正解値の差ではなく、GradDiff モデルと再学習モデルの予測値の差です。$L_f$ は忘却対象への当てはまりを、$\mathrm{RMS}_f$ は忘却対象での再学習モデルへの近さを、$\mathrm{RMS}_r$ は保持対象での予測変化を表します。値の意味が異なるため、3 指標を併記します。
2. 線形回帰の実験条件
実行環境として Python 3.12 と NumPy 2.2.6 を指定します。仮想環境を作成し、バージョンを指定して NumPy をインストールします。
python3.12 -m venv .venv
source .venv/bin/activate
python -m pip install numpy==2.2.6
python --version
python -c 'import numpy; print(numpy.__version__)'
入力は特徴量 2 個の固定データです。保持対象 4 件と忘却対象 1 件を使い、乱数は使用しません。
X_r=\begin{pmatrix}1&0\\0&1\\1&1\\2&1\end{pmatrix},\quad
y_r=\begin{pmatrix}1\\2\\3\\4\end{pmatrix},\qquad
X_f=\begin{pmatrix}1&0\end{pmatrix},\quad y_f=\begin{pmatrix}5\end{pmatrix}
全データ数は $n=5$、忘却対象数は $m=1$、L2 正則化係数は $\lambda=0.1$ とします。保持対象の目的関数 $\mathcal R$ と忘却対象の目的関数 $\mathcal F$ を次のように置きます。
\mathcal R(\theta)=\frac{1}{2n}\|X_r\theta-y_r\|_2^2+\frac{\lambda}{2}\|\theta\|_2^2
\mathcal F(\theta)=\frac{1}{2n}\|X_f\theta-y_f\|_2^2
保持対象だけで再学習する場合にも、分母を元の全データ数 $n=5$ に固定します。忘却損失として表示する $L_f$ は 1 件当たりの値であり、最適化に使用する $\mathcal F$ とは正規化が異なります。この例では $L_f=5\mathcal F$ です。
GradDiff の目的関数は次のとおりです。
J_\rho(\theta)=\mathcal R(\theta)-\rho\mathcal F(\theta)
$\rho=0$ では保持対象だけで再学習する目的関数と一致します。$\rho>0$ では、忘却対象の損失を増やす項の寄与が加わります。
3. 元モデルと再学習モデルを求める
線形回帰の最小解は連立一次方程式から求められます。再学習モデルの係数は次を満たします。
H_r\theta_r=b_r,\qquad
H_r=\frac{X_r^\top X_r}{n}+\lambda I,\qquad
b_r=\frac{X_r^\top y_r}{n}
全データで学習する元モデルは、上式の $X_r,y_r$ を全データの $X,y$ に置き換えて計算します。GradDiff については、$J_\rho$ の勾配を 0 と置くと次が得られます。
M_\rho\theta_\rho=b_r-\frac{\rho}{n}X_f^\top y_f,\qquad
M_\rho=H_r-\frac{\rho}{n}X_f^\top X_f
$M_\rho$ が正定値であれば、$J_\rho$ は厳密に凸で、一意の最小解を持ちます。NumPy の numpy.linalg.solve() は連立一次方程式 $Ax=b$ を解く関数です[3]。この実験では逆行列を明示的に計算せず、solve() を使用します。
次のコード全体を grad_diff_linear.py に保存します。元モデルと再学習モデルを求め、忘却係数ごとに GradDiff の解、3 指標、ヘッセ行列の最小固有値を出力します。さらに、自己レバレッジと交差レバレッジから導いた出力差の式との一致を assert で確認します。
import numpy as np
X_r = np.array([[1., 0.], [0., 1.], [1., 1.], [2., 1.]])
y_r = np.array([1., 2., 3., 4.])
X_f = np.array([[1., 0.]])
y_f = np.array([5.])
n = len(X_r) + len(X_f)
lam = 0.1
ident = np.eye(X_r.shape[1])
X_all = np.vstack((X_r, X_f))
y_all = np.concatenate((y_r, y_f))
H_all = X_all.T @ X_all / n + lam * ident
b_all = X_all.T @ y_all / n
theta_all = np.linalg.solve(H_all, b_all)
H_r = X_r.T @ X_r / n + lam * ident
b_r = X_r.T @ y_r / n
theta_r = np.linalg.solve(H_r, b_r)
K_ff = X_f @ np.linalg.solve(H_r, X_f.T)
K_rf = X_r @ np.linalg.solve(H_r, X_f.T)
e_f = X_f @ theta_r - y_f
rho_crit = n / np.linalg.eigvalsh(K_ff)[-1]
print(f"theta_all: {theta_all}")
print(f"theta_retrained: {theta_r}")
print(f"rho_crit: {rho_crit:.6f}")
print("rho forget_loss RMS_f RMS_r min_eig")
for rho in (0.0, 0.1, 0.5, 1.0, 3.5, 3.9):
M = H_r - rho * (X_f.T @ X_f) / n
min_eig = np.linalg.eigvalsh(M)[0]
assert min_eig > 0.0
b = b_r - rho * (X_f.T @ y_f) / n
theta = np.linalg.solve(M, b)
delta_f = X_f @ (theta - theta_r)
delta_r = X_r @ (theta - theta_r)
forget_loss = 0.5 * np.mean((X_f @ theta - y_f) ** 2)
rms_f = np.sqrt(np.mean(delta_f ** 2))
rms_r = np.sqrt(np.mean(delta_r ** 2))
gain = np.linalg.solve(np.eye(len(X_f)) - rho * K_ff / n, e_f)
expected_f = rho * (K_ff @ gain) / n
expected_r = rho * (K_rf @ gain) / n
assert np.allclose(delta_f, expected_f)
assert np.allclose(delta_r, expected_r)
print(f"{rho:3.1f} {forget_loss:11.6f} {rms_f:9.6f} "
f"{rms_r:9.6f} {min_eig:9.6f}")
次のコマンドで実行します。
python grad_diff_linear.py
全データで学習した係数はおよそ $(1.884058,\ 0.956522)$、忘却対象を除いて再学習した係数はおよそ $(1.090909,\ 1.636364)$ です。この二つのモデルが異なるため、忘却処理後のモデルを評価するときには比較基準を明示する必要があります。
4. GradDiff の忘却係数を変更する
コードでは $\rho$ を 0.0、0.1、0.5、1.0、3.5、3.9 として計算します。各係数について、$M_\rho$ の最小固有値が正であることを確認してから $\theta_\rho$ を求めます。
rho=0.0 は保持対象だけでの再学習に一致する基準点です。rho=0.1 から 1.0 では、忘却損失と出力差がどのように変化するかを比較します。rho=3.5 と 3.9 は、正定値性の境界へ近づいた場合の出力差を確認するための値です。
このコードで計算するのは、GradDiff の二次目的関数の厳密な最小解です。学習済みの元モデルから勾配更新を反復する実装とは区別します。したがって、反復回数、学習率、早期終了条件が結果に与える影響は、この実験の評価対象外です。
5. 忘却損失と二つの出力差を比較する
出力のうち、$\rho\leq1$ の結果は次のとおりです。小数点以下 6 桁に丸めています。
| 忘却係数 $\rho$ | 忘却損失 $L_f$ | $\mathrm{RMS}_f$ | $\mathrm{RMS}_r$ | $M_\rho$ の最小固有値 |
|---|---|---|---|---|
| 0.0 | 7.640496 | 0.000000 | 0.000000 | 0.329180 |
| 0.1 | 8.044839 | 0.102103 | 0.089322 | 0.323592 |
| 0.5 | 10.031467 | 0.570076 | 0.498712 | 0.300000 |
| 1.0 | 13.749256 | 1.334812 | 1.167717 | 0.267544 |
$\rho=0$ の両 RMS が 0 なのは、GradDiff モデルと再学習モデルが同じ最適解になるためです。$\rho=1$ では忘却損失が増える一方、忘却対象の出力は再学習モデルから 1.334812 離れています。保持対象にも 1.167717 の出力差が生じています。
忘却損失の増加は、忘却対象の正解値から予測値が離れたことを示します。一方、再学習モデルへの接近を判断する量は $\mathrm{RMS}_f$ です。また、保持対象の予測がどれだけ変化したかは $\mathrm{RMS}_r$ から確認できます。今回の固定データでは、忘却損失の増加と二つの RMS の増加が同時に観測されました。
6. 忘却係数の上限を確認する
GradDiff の最小解が一意に存在する範囲は、$M_\rho$ の正定値性で判定します。numpy.linalg.eigvalsh() は対称行列の固有値を求める関数です[4]。コードでは $M_\rho$ の最小固有値を調べています。
忘却対象の自己レバレッジ行列を次のように定義します。これは、忘却対象の特徴が保持対象の損失の曲率に対してどれほど大きく作用するかを表す行列です。
K_{ff}=X_fH_r^{-1}X_f^\top
線形回帰における GradDiff の正定値条件は、$K_{ff}$ の最大固有値を使って次のように書けます[5]。
\frac{\rho}{n}\lambda_{\max}(K_{ff})<1
今回のデータでは $K_{ff}=14/11$、$n=5$ なので、境界は次の値です。
\rho_{\mathrm{crit}}=\frac{5}{14/11}=\frac{55}{14}\approx3.928571
境界に近い 2 点の計算結果を示します。
| 忘却係数 $\rho$ | 忘却損失 $L_f$ | $\mathrm{RMS}_f$ | $\mathrm{RMS}_r$ | $M_\rho$ の最小固有値 |
|---|---|---|---|---|
| 3.5 | 642.013889 | 31.924242 | 27.927894 | 0.047920 |
| 3.9 | 144453.125000 | 533.590909 | 466.794807 | 0.003288 |
$\rho=3.9$ でも最小固有値は正なので、一意の最小解が存在します。ただし、境界へ近づくと忘却対象の残差が増幅され、両方の出力差が増大しています。さらに境界では $M_\rho$ が特異となり、境界を超えるとこの二次目的関数は下に有界ではなくなります。
この影響は、再学習モデルの忘却対象残差 $e_f=X_f\theta_r-y_f$ と、交差レバレッジ $K_{rf}=X_rH_r^{-1}X_f^\top$ を使って、次の出力差で表せます。
\Delta_f=\frac{\rho}{n}K_{ff}\left(I-\frac{\rho}{n}K_{ff}\right)^{-1}e_f
\Delta_r=\frac{\rho}{n}K_{rf}\left(I-\frac{\rho}{n}K_{ff}\right)^{-1}e_f
忘却対象の残差は自己レバレッジで増幅され、その変位が交差レバレッジを通じて保持対象の予測にも伝わります。コード内の expected_f と expected_r はこの式の計算結果で、assert np.allclose(...) により、直接求めた出力差と一致することを検証しています。
7. 検証結果から判断できること
GradDiff の忘却損失は、指定データに対する予測誤差の増加を測ります。忘却対象を除いて再学習した状態にどれほど近いかは、同じ入力集合に対する再学習モデルとの出力差で判断します。保持対象での出力差も別に測定することで、忘却対象を変更した影響が保持すべき予測へ及ぶ程度を確認できます。
今回の実験では、忘却係数を 0.0 から 1.0 へ増やす間、忘却損失と二つの RMS がいずれも増加しました。さらに 3.9 では、GradDiff の最小解が一意に存在する範囲でも出力差が大きくなることを確認できました。
実装上は、忘却損失 $L_f$ の推移に加え、$\mathrm{RMS}_f$ と $\mathrm{RMS}r$ を別々に記録し、$M\rho$ の正定値性を確認することが、今回の線形回帰に対する評価手順です。これらは固定した入力集合での予測上の差を測る指標であり、モデル内部から個別データの情報が完全に除去されたという保証とは区別します。
参考文献
- Maini, P. et al., TOFU: A Task of Fictitious Unlearning for LLMs(2024-01-11). https://arxiv.org/abs/2401.06121
- id774, 忘却は、再学習との差と保持データへの影響で測る(2026-11-05). https://blog.id774.net/entry/2026/11/05/5838/
- NumPy Developers, numpy.linalg.solve. https://numpy.org/doc/2.2/reference/generated/numpy.linalg.solve.html
- NumPy Developers, numpy.linalg.eigvalsh. https://numpy.org/doc/2.2/reference/generated/numpy.linalg.eigvalsh.html
- Zhao, X., Amiri, M. M., Magdon-Ismail, M., Loss is Not Behavior: A Unified Output-Space Analysis of Gradient-Based Machine Unlearning(2026-10-28). https://arxiv.org/abs/2610.38784v1