muooon commited on
Commit
03442f4
·
verified ·
1 Parent(s): b50351c

Upload 3 files

Browse files
Files changed (3) hide show
  1. 1Gv31_AMP/emofactx.py +170 -0
  2. 1Gv31_AMP/emolynxx.py +174 -0
  3. 1Gv31_AMP/emonavix.py +155 -0
1Gv31_AMP/emofactx.py ADDED
@@ -0,0 +1,170 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from torch.optim import Optimizer
3
+ import math
4
+ from collections import deque
5
+
6
+ """
7
+ EmoFact v3.1 (251125) shadow-system v3.0 -effect NoN -moment v3.0
8
+ (v1.0)AMP対応完了(250725) p.data -> p 修正済み/低精度量子化への基本対応/低精度補償は別
9
+ (v2.0)shadow-system 微調整/3段階補正を連続的に滑らかに/派生版では以下の切替も可能
10
+ optimizer 指定の際に True / False で shadow を切替できる(現在 False)
11
+ (v3.0)emosens shadow-effect v1.0 反映した動的学習率と shadow-system 切替をデフォルト化
12
+ (v3.1)通常未使用の shadow 更新速度 (lerp) を倍化し信頼度で動的制御/coeff 活用(急変・微動)
13
+ 動的学習率や感情スカラー値など TensorBoard 連携可 (現在 writer=None)/外部設定必要
14
+ 全体の効率化や可読性を向上(emaやスカラーの多重処理を省く等、動的学習率のスケールや状態の見直し等、含む)
15
+ """
16
+
17
+ class EmoFact(Optimizer):
18
+ # クラス定義&初期化
19
+ def __init__(self, params, lr=1e-3, betas=(0.9, 0.999), eps=1e-8,
20
+ weight_decay=0.01, use_shadow: bool = False, writer=None):
21
+ defaults = dict(lr=lr, betas=betas, eps=eps, weight_decay=weight_decay)
22
+ super().__init__(params, defaults)
23
+ self._init_lr = lr
24
+ self.should_stop = False # 停止フラグの初期化
25
+ self.use_shadow = use_shadow # 🔸shadow 使用フラグを保存
26
+ self.writer = writer # 動的学習率や感情スカラー等を渡す
27
+
28
+ # 感情EMA更新(緊張と安静)
29
+ def _update_ema(self, state, loss_val):
30
+ ema = state.setdefault('ema', {})
31
+ ema['short'] = 0.3 * loss_val + 0.7 * ema.get('short', loss_val)
32
+ ema['long'] = 0.01 * loss_val + 0.99 * ema.get('long', loss_val)
33
+ return ema
34
+
35
+ # 感情スカラー値生成(EMA差分、滑らかな非線形スカラー、tanh(diff) は ±1.0 で有界性)
36
+ # 係数"1":ema差分 のスケール調整処理に活用(感度調節係数)/通常は1(タスクに応じ調整可(非推奨))
37
+ # scale_base:Loss値とema値の乖離を修正(分母 ema(long) 「改善率」共通化/loss種に非依存)
38
+ # 1e-5(デフォルト)/1e-6(感度向上)/1e-4(安定性向上):分母を0にせず安定させる
39
+ def _compute_scalar(self, ema):
40
+ scale_base = max(ema['long'], 1e-5)
41
+ diff = (ema['long'] - ema['short']) / scale_base
42
+ return math.tanh(1 * diff)
43
+
44
+ # 急変時は論文通りの抑制則/悪化時は減速/改善時は加速/微動時は無介入で収束を安定させる
45
+ def _decide_coeff(self, scalar):
46
+ if abs(scalar) > 0.625:
47
+ return 1.0 - abs(scalar) # 急変|強抑制
48
+ elif scalar > 0.125:
49
+ return 1.0 + scalar # 改善|加速
50
+ elif scalar < -0.125:
51
+ return 1.0 + scalar # 悪化|減速
52
+ else:
53
+ return 1.0 # 微動|無介入
54
+
55
+ # (重要)現在は shadow-effect を参考に得た動的フィルタ効果の近似により use_shadow=False です
56
+ # しかし全機能は shadow なしで全て成立します/コード確認時はこの shadow を考慮外として無視してください
57
+
58
+ # Shadow混合比 3段階構成 タスクに応じ調整可、以下を参考に 開始値・範囲量・変化幅を調整
59
+ # 参考1:scalar>±0.6 を "return 開始値 + ((scalar) - 0.6(範囲)) / 範囲量 * 変化幅"
60
+ # 参考2:scalar>±0.1 を "return 開始値 + ((scalar) - 0.1(範囲)) / 範囲量 * 変化幅"
61
+ # return 開始値 + ((scalar) - 閾値) / 範囲量 * 変化幅 です(上記の値は感情スカラーを返すだけ)
62
+ def _decide_ratio(self, scalar):
63
+ if not self.use_shadow:
64
+ return 0.0 # 🔸use_shadow = False のとき常に比率を 0 にする
65
+ if abs(scalar) > 0.75:
66
+ return 0.75 # + ((scalar) - 0.75) / 0.4 * 0.4 # これはスカラーそのまま返す参考例
67
+ elif abs(scalar) > 0.25:
68
+ return -0.1 # return<0 の場合は leap 専用(書き戻しはしないが履歴更新のみ)
69
+ return 0.0
70
+
71
+ # 損失取得(損失値 loss_val を数値化、感情判定に使用、存在しないパラメータ(更新不要)はスキップ)
72
+ @torch.no_grad()
73
+ def step(self, closure=None):
74
+ loss = closure() if closure is not None else None
75
+ loss_val = loss.item() if loss is not None else 0.0
76
+
77
+ # EMA更新・スカラー生成(EMA差分からスカラーを生成しスパイク比率等を決定)
78
+ ema = self._update_ema(self.state, loss_val)
79
+ scalar = self._compute_scalar(ema)
80
+ coeff = self._decide_coeff(scalar)
81
+ ratio = self._decide_ratio(scalar)
82
+
83
+ for group in self.param_groups:
84
+ for p in group['params']:
85
+ if p.grad is None:
86
+ continue
87
+
88
+ grad = p.grad
89
+ state = self.state[p]
90
+
91
+ # 動的学習率補正により shadow 形成を信頼度で調整(coeffは正値(負にならない))
92
+ # shadow:必要時のみ(スパイクp部分に現在値を最大10%追従させる動的履歴更新)
93
+ # ratio <0:10%、0以外:10%×coeff、(0.25~0.75は10%、微動と急変は*coeff)
94
+ # 微動時 coeff:1.0 固定なので結果的に微動時も 10% 履歴更新になる
95
+ # 結果、微動時と安定時:10%、急変時:coeff、による履歴更新を行うことになる
96
+ if self.use_shadow:
97
+ if 'shadow' not in state: # 🔸shadow = False (デフォルト)
98
+ state['shadow'] = p.clone()
99
+ if ratio > 0: # 書き戻しと履歴更新(急変時の強い抑制と弱めの履歴更新)
100
+ p.mul_(1 - ratio).add_(state['shadow'], alpha=coeff)
101
+ else: # 書き戻しせず履歴更新のみ:ratio<0:10%/0以外:10%×coeff
102
+ leap_ratio = 0.1 if ratio < 0 else 0.1 * coeff
103
+ state['shadow'].lerp_(p, leap_ratio)
104
+
105
+ # 上記 shadow の説明:スカラー生成:短期と長期EMAの差分から信号を得る(高ぶりの強さ)
106
+ # 混合比率:スカラーが閾値を超える場合にのみ計算される(信頼できる感情信号かどうかの選別)
107
+ # 急変時は感情機構による shadow 混合で強く抑制する(急制動による安定性の確保)
108
+ # 新しい shadow-system は動的学習率と協調することで選択的スパース性も発揮する
109
+
110
+ # --- Start Fact Gradient Update Logic ---
111
+ # 行列の形状が2次元以上の場合、分散情報ベースのAB近似を使用
112
+ if grad.dim() >= 2:
113
+ # 行と列の2乗平均を計算 (分散の軽量な近似)
114
+ r_sq = torch.mean(grad * grad, dim=tuple(range(1, grad.dim())), keepdim=True).add_(group['eps'])
115
+ c_sq = torch.mean(grad * grad, dim=0, keepdim=True).add_(group['eps'])
116
+
117
+ # 分散情報から勾配の近似行列を生成
118
+ # AB行列として見立てたものを直接生成し更新項を計算する
119
+ # A = sqrt(r_sq), B = sqrt(c_sq) AB行列の近似を再現しEMAで平滑化する
120
+ beta1, beta2 = group['betas']
121
+ state.setdefault('exp_avg_r', torch.zeros_like(r_sq)).mul_(beta1).add_(torch.sqrt(r_sq), alpha=1 - beta1)
122
+ state.setdefault('exp_avg_c', torch.zeros_like(c_sq)).mul_(beta1).add_(torch.sqrt(c_sq), alpha=1 - beta1)
123
+
124
+ # 再構築した近似勾配の平方根の積で正規化
125
+ denom = torch.sqrt(state['exp_avg_r'] * state['exp_avg_c']).add_(group['eps'])
126
+
127
+ # 最終的な更新項を計算
128
+ update_term = grad / denom
129
+
130
+ # 1次元(ベクトル)の勾配補正
131
+ else:
132
+ beta1, beta2 = group['betas']
133
+ exp_avg_sq = state.setdefault('exp_avg_sq', torch.zeros_like(p))
134
+ exp_avg_sq.mul_(beta1).addcmul_(grad, grad, value=(1 - beta2))
135
+ denom = exp_avg_sq.sqrt().add_(group['eps'])
136
+ update_term = grad / denom
137
+
138
+ # 最終的なパラメータ更新 (decoupled weight decayも適用)
139
+ p.add_(p, alpha=-group['weight_decay'] * group['lr'])
140
+ p.add_(update_term, alpha=-group['lr'] * coeff)
141
+ # --- End Fact Gradient Update Logic ---
142
+
143
+ # 感情機構の発火が収まり"十分に安定"していることを外部伝達できる(自動停止ロジックではない)
144
+ # Early Stop用 scalar 記録(バッファ共通で管理/最大32件保持/動静評価)
145
+ hist = self.state.setdefault('scalar_hist', deque(maxlen=32))
146
+ hist.append(scalar)
147
+
148
+ # Early Stop判断(静けさの合図)
149
+ # 32ステップ分のスカラー値の静かな条件を満たした時"フラグ" should_stop = True になるだけ
150
+ if len(hist) >= 32:
151
+ avg_abs = sum(abs(s) for s in hist) / len(hist)
152
+ mean = sum(hist) / len(hist)
153
+ var = sum((s - mean)**2 for s in hist) / len(hist)
154
+ if avg_abs < 0.05 and var < 0.005:
155
+ self.should_stop = True # 💡 外部からこれを見て判断可
156
+
157
+ # TensorBoardへの記録(step関数の末尾に追加)
158
+ if hasattr(self, 'writer') and self.writer is not None:
159
+ self._step_count = getattr(self, "_step_count", 0) + 1
160
+ for i, group in enumerate(self.param_groups):
161
+ self.writer.add_scalar(f"Lr{i}", group['lr'], self._step_count)
162
+ self.writer.add_scalar("eScalar", scalar, self._step_count)
163
+
164
+ return loss
165
+
166
+ """
167
+ https://github.com/muooon/EmoNavi
168
+ Fact is inspired by Adafactor, and emoairy,
169
+ and its VRAM-friendly design is something everyone loves.
170
+ """
1Gv31_AMP/emolynxx.py ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from torch.optim import Optimizer
3
+ import math
4
+ from typing import Tuple, Callable, Union
5
+ from collections import deque
6
+
7
+ """
8
+ EmoLynx v3.1 (251125) shadow-system v3.0 -effect NoN -moment v3.0
9
+ (v1.0)AMP対応完了(250725) p.data -> p 修正済み/低精度量子化への基本対応/低精度補償は別
10
+ (v2.0)shadow-system 微調整/3段階補正を連続的に滑らかに/派生版では以下の切替も可能
11
+ optimizer 指定の際に True / False で shadow を切替できる(現在 False)
12
+ (v3.0)emosens shadow-effect v1.0 反映した動的学習率と shadow-system 切替をデフォルト化
13
+ (v3.1)通常未使用の shadow 更新速度 (lerp) を倍化し信頼度で動的制御/coeff 活用(急変・微動)
14
+ 動的学習率や感情スカラー値など TensorBoard 連携可 (現在 writer=None)/外部設定必要
15
+ 全体の効率化や可読性を向上(emaやスカラーの多重処理を省く等、動的学習率のスケールや状態の見直し等、含む)
16
+ """
17
+
18
+ # Helper function (Lynx)
19
+ def exists(val):
20
+ return val is not None
21
+
22
+ class EmoLynx(Optimizer):
23
+ # クラス定義&初期化 lynx用ベータ・互換性の追加(lynx用beta1・beta2)
24
+ def __init__(self, params: Union[list, torch.nn.Module], lr=1e-3, eps=1e-8,
25
+ betas=(0.9, 0.999), weight_decay=0.01, use_shadow: bool = False,
26
+ writer=None):
27
+ defaults = dict(lr=lr, betas=betas, eps=eps, weight_decay=weight_decay)
28
+ super().__init__(params, defaults)
29
+ # lynxに応じてウェイト減衰のため保存
30
+ self._init_lr = lr
31
+ self.should_stop = False # 停止フラグの初期化
32
+ self.use_shadow = use_shadow # 🔸shadow 使用フラグを保存
33
+ self.writer = writer # 動的学習率や感情スカラー等を渡す
34
+
35
+ # 感情EMA更新(緊張と安静)
36
+ def _update_ema(self, state, loss_val):
37
+ ema = state.setdefault('ema', {})
38
+ ema['short'] = 0.3 * loss_val + 0.7 * ema.get('short', loss_val)
39
+ ema['long'] = 0.01 * loss_val + 0.99 * ema.get('long', loss_val)
40
+ return ema
41
+
42
+ # 感情スカラー値生成(EMA差分、滑らかな非線形スカラー、tanh(diff) は ±1.0 で有界性)
43
+ # 係数"1":ema差分 のスケール調整処理に活用(感度調節係数)/通常は1(タスクに応じ調整可(非推奨))
44
+ # scale_base:Loss値とema値の乖離を修正(分母 ema(long) 「改善率」共通化/loss種に非依存)
45
+ # 1e-5(デフォルト)/1e-6(感度向上)/1e-4(安定性向上):分母を0にせず安定させる
46
+ def _compute_scalar(self, ema):
47
+ scale_base = max(ema['long'], 1e-5)
48
+ diff = (ema['long'] - ema['short']) / scale_base
49
+ return math.tanh(1 * diff)
50
+
51
+ # 急変時は論文通りの抑制則/悪化時は減速/改善時は加速/微動時は無介入で収束を安定させる
52
+ def _decide_coeff(self, scalar):
53
+ if abs(scalar) > 0.625:
54
+ return 1.0 - abs(scalar) # 急変|強抑制
55
+ elif scalar > 0.125:
56
+ return 1.0 + scalar # 改善|加速
57
+ elif scalar < -0.125:
58
+ return 1.0 + scalar # 悪化|減速
59
+ else:
60
+ return 1.0 # 微動|無介入
61
+
62
+ # (重要)現在は shadow-effect を参考に得た動的フィルタ効果の近似により use_shadow=False です
63
+ # しかし全機能は shadow なしで全て成立します/コード確認時はこの shadow を考慮外として無視してください
64
+
65
+ # Shadow混合比 3段階構成 タスクに応じ調整可、以下を参考に 開始値・範囲量・変化幅を調整
66
+ # 参考1:scalar>±0.6 を "return 開始値 + ((scalar) - 0.6(範囲)) / 範囲量 * 変化幅"
67
+ # 参考2:scalar>±0.1 を "return 開始値 + ((scalar) - 0.1(範囲)) / 範囲量 * 変化幅"
68
+ # return 開始値 + ((scalar) - 閾値) / 範囲量 * 変化幅 です(上記の値は感情スカラーを返すだけ)
69
+ def _decide_ratio(self, scalar):
70
+ if not self.use_shadow:
71
+ return 0.0 # 🔸use_shadow = False のとき常に比率を 0 にする
72
+ if abs(scalar) > 0.75:
73
+ return 0.75 # + ((scalar) - 0.75) / 0.4 * 0.4 # これはスカラーそのまま返す参考例
74
+ elif abs(scalar) > 0.25:
75
+ return -0.1 # return<0 の場合は leap 専用(書き戻しはしないが履歴更新のみ)
76
+ return 0.0
77
+
78
+ # 損失取得(損失値 loss_val を数値化、感情判定に使用、存在しないパラメータ(更新不要)はスキップ)
79
+ @torch.no_grad()
80
+ def step(self, closure: Callable | None = None): # クロージャの型ヒントを追加
81
+ loss = None
82
+ if exists(closure): # 一貫性のためにexistsヘルパーを使う
83
+ with torch.enable_grad():
84
+ loss = closure()
85
+ loss_val = loss.item() if loss is not None else 0.0
86
+
87
+ # EMA更新・スカラー生成(EMA差分からスカラーを生成しスパイク比率等を決定)
88
+ ema = self._update_ema(self.state, loss_val)
89
+ scalar = self._compute_scalar(ema)
90
+ coeff = self._decide_coeff(scalar)
91
+ ratio = self._decide_ratio(scalar)
92
+
93
+ for group in self.param_groups:
94
+ # リンクス共通パラメータ抽出
95
+ lr, wd, beta1, beta2 = group['lr'], group['weight_decay'], *group['betas']
96
+
97
+ # ウェイト減衰の処理を分離 (from lynx)
98
+ _wd_actual = wd
99
+
100
+ for p in filter(lambda p: exists(p.grad), group['params']): # PGチェックにフィルタ
101
+
102
+ grad = p.grad # PG直接使用(計算に".data"不要)
103
+ state = self.state[p]
104
+
105
+ # 動的学習率補正により shadow 形成を信頼度で調整(coeffは正値(負にならない))
106
+ # shadow:必要時のみ(スパイクp部分に現在値を最大10%追従させる動的履歴更新)
107
+ # ratio <0:10%、0以外:10%×coeff、(0.25~0.75は10%、微動と急変は*coeff)
108
+ # 微動時 coeff:1.0 固定なので結果的に微動時も 10% 履歴更新になる
109
+ # 結果、微動時と安定時:10%、急変時:coeff、による履歴更新を行うことになる
110
+ if self.use_shadow:
111
+ if 'shadow' not in state: # 🔸shadow = False (デフォルト)
112
+ state['shadow'] = p.clone()
113
+ if ratio > 0: # 書き戻しと履歴更新(急変時の強い抑制と弱めの履歴更新)
114
+ p.mul_(1 - ratio).add_(state['shadow'], alpha=coeff)
115
+ else: # 書き戻しせず履歴更新のみ:ratio<0:10%/0以外:10%×coeff
116
+ leap_ratio = 0.1 if ratio < 0 else 0.1 * coeff
117
+ state['shadow'].lerp_(p, leap_ratio)
118
+
119
+ # 上記 shadow の説明:スカラー生成:短期と長期EMAの差分から信号を得る(高ぶりの強さ)
120
+ # 混合比率:スカラーが閾値を超える場合にのみ計算される(信頼できる感情信号かどうかの選別)
121
+ # 急変時は感情機構による shadow 混合で強く抑制する(急制動による安定性の確保)
122
+ # 新しい shadow-system は動的学習率と協調することで選択的スパース性も発揮する
123
+
124
+ # --- Start Lynx Gradient Update Logic ---
125
+ # lynx初期化(exp_avg_sq)
126
+ if 'exp_avg' not in state:
127
+ state['exp_avg'] = torch.zeros_like(p)
128
+ exp_avg = state['exp_avg']
129
+
130
+ # Stepweight decay (from lynx): p = p * (1 - lr * wd)
131
+ # decoupled_wd 考慮 _wd_actual 使用(EmoNaviのwdは最後に適用)
132
+ p.mul_(1 - lr * _wd_actual)
133
+ beta1, beta2 = group['betas']
134
+
135
+ # 勾配ブレンド
136
+ # m_t = beta1 * exp_avg_prev + (1 - beta1) * grad
137
+ blended_grad = grad.mul(1 - beta1).add_(exp_avg, alpha=beta1)
138
+
139
+ # p: p = p - lr * sign(blended_grad)
140
+ p.add_(blended_grad.sign_(), alpha = -lr * coeff)
141
+
142
+ # exp_avg = beta2 * exp_avg + (1 - beta2) * grad
143
+ exp_avg.mul_(beta2).add_(grad, alpha = 1 - beta2)
144
+ # --- End Lynx Gradient Update Logic ---
145
+
146
+ # 感情機構の発火が収まり"十分に安定"していることを外部伝達できる(自動停止ロジックではない)
147
+ # Early Stop用 scalar 記録(バッファ共通で管理/最大32件保持/動静評価)
148
+ hist = self.state.setdefault('scalar_hist', deque(maxlen=32))
149
+ hist.append(scalar)
150
+
151
+ # Early Stop判断(静けさの合図)
152
+ # 32ステップ分のスカラー値の静かな条件を満たした時"フラグ" should_stop = True になるだけ
153
+ if len(hist) >= 32:
154
+ avg_abs = sum(abs(s) for s in hist) / len(hist)
155
+ mean = sum(hist) / len(hist)
156
+ var = sum((s - mean)**2 for s in hist) / len(hist)
157
+ if avg_abs < 0.05 and var < 0.005:
158
+ self.should_stop = True # 💡 外部からこれを見て判断可
159
+
160
+ # TensorBoardへの記録(step関数の末尾に追加)
161
+ if hasattr(self, 'writer') and self.writer is not None:
162
+ self._step_count = getattr(self, "_step_count", 0) + 1
163
+ for i, group in enumerate(self.param_groups):
164
+ self.writer.add_scalar(f"Lr{i}", group['lr'], self._step_count)
165
+ self.writer.add_scalar("eScalar", scalar, self._step_count)
166
+
167
+ return loss
168
+
169
+ """
170
+ https://github.com/muooon/EmoNavi
171
+ Lynx was developed with inspiration from Lion, Tiger, and emocats,
172
+ which we deeply respect for their lightweight and intelligent design.
173
+ Lynx also integrates EmoNAVI to enhance its capabilities.
174
+ """
1Gv31_AMP/emonavix.py ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from torch.optim import Optimizer
3
+ import math
4
+ from collections import deque
5
+
6
+ """
7
+ EmoNavi v3.1 (251125) shadow-system v3.0 -effect NoN -moment v3.0
8
+ (v1.0)AMP対応完了(250725) p.data -> p 修正済み/低精度量子化への基本対応/低精度補償は別
9
+ (v2.0)shadow-system 微調整/3段階補正を連続的に滑らかに/派生版では以下の切替も可能
10
+ optimizer 指定の際に True / False で shadow を切替できる(現在 False)
11
+ (v3.0)emosens shadow-effect v1.0 反映した動的学習率と shadow-system 切替をデフォルト化
12
+ (v3.1)通常未使用の shadow 更新速度 (lerp) を倍化し信頼度で動的制御/coeff 活用(急変・微動)
13
+ 動的学習率や感情スカラー値など TensorBoard 連携可 (現在 writer=None)/外部設定必要
14
+ 全体の効率化や可読性を向上(emaやスカラーの多重処理を省く等、動的学習率のスケールや状態の見直し等、含む)
15
+ """
16
+
17
+ class EmoNavi(Optimizer):
18
+ # クラス定義&初期化
19
+ def __init__(self, params, lr=1e-3, betas=(0.9, 0.999), eps=1e-8,
20
+ weight_decay=0.01, use_shadow: bool = False, writer=None):
21
+ defaults = dict(lr=lr, betas=betas, eps=eps, weight_decay=weight_decay)
22
+ super().__init__(params, defaults)
23
+ self._init_lr = lr
24
+ self.should_stop = False # 停止フラグの初期化
25
+ self.use_shadow = use_shadow # 🔸shadow 使用フラグを保存
26
+ self.writer = writer # 動的学習率や感情スカラー等を渡す
27
+
28
+ # 感情EMA更新(緊張と安静)
29
+ def _update_ema(self, state, loss_val):
30
+ ema = state.setdefault('ema', {})
31
+ ema['short'] = 0.3 * loss_val + 0.7 * ema.get('short', loss_val)
32
+ ema['long'] = 0.01 * loss_val + 0.99 * ema.get('long', loss_val)
33
+ return ema
34
+
35
+ # 感情スカラー値生成(EMA差分、滑らかな非線形スカラー、tanh(diff) は ±1.0 で有界性)
36
+ # 係数"1":ema差分 のスケール調整処理に活用(感度調節係数)/通常は1(タスクに応じ調整可(非推奨))
37
+ # scale_base:Loss値とema値の乖離を修正(分母 ema(long) 「改善率」共通化/loss種に非依存)
38
+ # 1e-5(デフォルト)/1e-6(感度向上)/1e-4(安定性向上):分母を0にせず安定させる
39
+ def _compute_scalar(self, ema):
40
+ scale_base = max(ema['long'], 1e-5)
41
+ diff = (ema['long'] - ema['short']) / scale_base
42
+ return math.tanh(1 * diff)
43
+
44
+ # 急変時は論文通りの抑制則/悪化時は減速/改善時は加速/微動時は無介入で収束を安定させる
45
+ def _decide_coeff(self, scalar):
46
+ if abs(scalar) > 0.625:
47
+ return 1.0 - abs(scalar) # 急変|強抑制
48
+ elif scalar > 0.125:
49
+ return 1.0 + scalar # 改善|加速
50
+ elif scalar < -0.125:
51
+ return 1.0 + scalar # 悪化|減速
52
+ else:
53
+ return 1.0 # 微動|無介入
54
+
55
+ # (重要)現在は shadow-effect を参考に得た動的フィルタ効果の近似により use_shadow=False です
56
+ # しかし全機能は shadow なしで全て成立します/コード確認時はこの shadow を考慮外として無視してください
57
+
58
+ # Shadow混合比 3段階構成 タスクに応じ調整可、以下を参考に 開始値・範囲量・変化幅を調整
59
+ # 参考1:scalar>±0.6 を "return 開始値 + ((scalar) - 0.6(範囲)) / 範囲量 * 変化幅"
60
+ # 参考2:scalar>±0.1 を "return 開始値 + ((scalar) - 0.1(範囲)) / 範囲量 * 変化幅"
61
+ # return 開始値 + ((scalar) - 閾値) / 範囲量 * 変化幅 です(上記の値は感情スカラーを返すだけ)
62
+ def _decide_ratio(self, scalar):
63
+ if not self.use_shadow:
64
+ return 0.0 # 🔸use_shadow = False のとき常に比率を 0 にする
65
+ if abs(scalar) > 0.75:
66
+ return 0.75 # + ((scalar) - 0.75) / 0.4 * 0.4 # これはスカラーそのまま返す参考例
67
+ elif abs(scalar) > 0.25:
68
+ return -0.1 # return<0 の場合は leap 専用(書き戻しはしないが履歴更新のみ)
69
+ return 0.0
70
+
71
+ # 損失取得(損失値 loss_val を数値化、感情判定に使用、存在しないパラメータ(更新不要)はスキップ)
72
+ @torch.no_grad()
73
+ def step(self, closure=None):
74
+ loss = closure() if closure is not None else None
75
+ loss_val = loss.item() if loss is not None else 0.0
76
+
77
+ # EMA更新・スカラー生成(EMA差分からスカラーを生成しスパイク比率等を決定)
78
+ ema = self._update_ema(self.state, loss_val)
79
+ scalar = self._compute_scalar(ema)
80
+ coeff = self._decide_coeff(scalar)
81
+ ratio = self._decide_ratio(scalar)
82
+
83
+ for group in self.param_groups:
84
+ for p in group['params']:
85
+ if p.grad is None:
86
+ continue
87
+
88
+ grad = p.grad
89
+ state = self.state[p]
90
+
91
+ # 動的学習率補正により shadow 形成を信頼度で調整(coeffは正値(負にならない))
92
+ # shadow:必要時のみ(スパイクp部分に現在値を最大10%追従させる動的履歴更新)
93
+ # ratio <0:10%、0以外:10%×coeff、(0.25~0.75は10%、微動と急変は*coeff)
94
+ # 微動時 coeff:1.0 固定なので結果的に微動時も 10% 履歴更新になる
95
+ # 結果、微動時と安定時:10%、急変時:coeff、による履歴更新を行うことになる
96
+ if self.use_shadow:
97
+ if 'shadow' not in state: # 🔸shadow = False (デフォルト)
98
+ state['shadow'] = p.clone()
99
+ if ratio > 0: # 書き戻しと履歴更新(急変時の強い抑制と弱めの履歴更新)
100
+ p.mul_(1 - ratio).add_(state['shadow'], alpha=coeff)
101
+ else: # 書き戻しせず履歴更新のみ:ratio<0:10%/0以外:10%×coeff
102
+ leap_ratio = 0.1 if ratio < 0 else 0.1 * coeff
103
+ state['shadow'].lerp_(p, leap_ratio)
104
+
105
+ # 上記 shadow の説明:スカラー生成:短期と長期EMAの差分から信号を得る(高ぶりの強さ)
106
+ # 混合比率:スカラーが閾値を超える場合にのみ計算される(信頼できる感情信号かどうかの選別)
107
+ # 急変時は感情機構による shadow 混合で強く抑制する(急制動による安定性の確保)
108
+ # 新しい shadow-system は動的学習率と協調することで選択的スパース性も発揮する
109
+
110
+ # --- Start Navi Gradient Update Logic ---
111
+ # 1次・2次モーメントを使った勾配補正(decoupled weight decay 構造に近い)
112
+ exp_avg = state.setdefault('exp_avg', torch.zeros_like(p))
113
+ exp_avg_sq = state.setdefault('exp_avg_sq', torch.zeros_like(p))
114
+ beta1, beta2 = group['betas']
115
+
116
+ exp_avg.mul_(beta1).add_(grad, alpha=1 - beta1)
117
+ exp_avg_sq.mul_(beta2).addcmul_(grad, grad, value=1 - beta2)
118
+ denom = exp_avg_sq.sqrt().add_(group['eps'])
119
+
120
+ step_size = group['lr']
121
+
122
+ # 最終的なパラメータ更新 (decoupled weight decay 適用)
123
+ if group['weight_decay']:
124
+ p.add_(p, alpha=-group['weight_decay'] * step_size)
125
+ p.addcdiv_(exp_avg, denom, value=-step_size * coeff)
126
+ # --- End Navi Gradient Update Logic ---
127
+
128
+ # 感情機構の発火が収まり"十分に安定"していることを外部伝達できる(自動停止ロジックではない)
129
+ # Early Stop用 scalar 記録(バッファ共通で管理/最大32件保持/動静評価)
130
+ hist = self.state.setdefault('scalar_hist', deque(maxlen=32))
131
+ hist.append(scalar)
132
+
133
+ # Early Stop判断(静けさの合図)
134
+ # 32ステップ分のスカラー値の静かな条件を満たした時"フラグ" should_stop = True になるだけ
135
+ if len(hist) >= 32:
136
+ avg_abs = sum(abs(s) for s in hist) / len(hist)
137
+ mean = sum(hist) / len(hist)
138
+ var = sum((s - mean)**2 for s in hist) / len(hist)
139
+ if avg_abs < 0.05 and var < 0.005:
140
+ self.should_stop = True # 💡 外部からこれを見て判断可
141
+
142
+ # TensorBoardへの記録(step関数の末尾に追加)
143
+ if hasattr(self, 'writer') and self.writer is not None:
144
+ self._step_count = getattr(self, "_step_count", 0) + 1
145
+ for i, group in enumerate(self.param_groups):
146
+ self.writer.add_scalar(f"Lr{i}", group['lr'], self._step_count)
147
+ self.writer.add_scalar("eScalar", scalar, self._step_count)
148
+
149
+ return loss
150
+
151
+ """
152
+ https://github.com/muooon/EmoNavi
153
+ An emotion-driven optimizer that feels loss and navigates accordingly.
154
+ Don't think. Feel. Don't stop. Keep running. Believe in what's beyond.
155
+ """