Dans ICT-12, la source a une valence intrinsèque parce que le schéma la place là : le champ gaussien attractif est donné d’entrée. C’est la lecture programmée de la valence (valence_at).
La question #7740 est l’envers exact : une valence qui est
apprise — acquise par conditionnement pavlovien, pas inscrite dans le schéma ;
transférable — un signal d’abord neutre devient attractif après association répétée avec une source biologiquement pertinente ;
distincte de la représentation prédictive — p̂prédit où est la source ; π encode combien l’agent investit dans un signal.
Ces deux lectures coexistent sans se confondre. Ici on outille la seconde (module ict/learned_valence.py, mergé #8823) : une règle de type Rescorla-Wagner où le changement de valence est proportionnel à l’erreur de prédiction, pas à la simple co-occurrence.
Le fait qu’acquisition et extinction partagent la même règle (l’erreur de prédiction) est précisément ce qui rend la valence réversible — pas deux systèmes, un seul qui s’adapte au signe de l’erreur. C’est la différence avec un simple compteur d’apparitions (qui ne s’éteindrait jamais).
Statut épistémique — Établi : La valence est apprise (Rescorla-Wagner), transférable et distincte de la prédiction p̂ (banc désincarné). Portée et détail dans la matrice de dissociations.
À l’initialisation, aucun signal n’est attractif : \(\pi = 0\) pour tout. C’est par construction — rien n’est investi avant tout apprentissage. Conditionnons un signal neutre en le présentant répétitivement avec une source pertinente (\(v_\text{source} = 1\)) et observons sa valence monter vers 1.
# Conditionnement : le signal 2 est neutre, puis co-occurre avec une source (valence 1).lv = LearnedValence(n_signals=5, lr=0.05)trace = []for step inrange(200): lv.condition(2, source_valence=1.0, steps=1) trace.append(lv.valence(2))fig, ax = plt.subplots(1, 2, figsize=(11, 3.6))ax[0].plot(trace, lw=2)ax[0].axhline(1.0, ls="--", color="k", alpha=0.4, label=r"$v_\mathrm{source}=1$")ax[0].set_xlabel("pas de conditionnement")ax[0].set_ylabel(r"$\pi_2$(valence apprise)")ax[0].set_title("Acquisition : un signal neutre devient attractif")ax[0].legend()# Vecteur pi final : seul le signal 2 a monté (pas de fuite vers les autres).ax[1].bar(range(5), lv.valence_vector(), color="C1")ax[1].set_xticks(range(5))ax[1].set_ylabel(r"$\pi_i$")ax[1].set_title("Pas de fuite : seul le signal conditionné est attractif")plt.tight_layout()plt.show()print(f"pi_2 final = {lv.valence(2):.4f} (cible 1.0) ; autres signaux = {lv.valence_vector()[[0,1,3,4]].tolist()}")
pi_2 final = 1.0000 (cible 1.0) ; autres signaux = [0.0, 0.0, 0.0, 0.0]
2. Le verdict de TRANSFERT
L’enjeu n’est pas qu’un signal monte quand on le renforce — c’est qu’il devienne attractif par lui-même, présenté seul. Et cruciallement : un signal jamais associé doit rester neutre. Sinon ce n’est pas du transfert, c’est une fuite de l’inné vers tout.
Le banc valence_transfer_test rend ce verdict falsifiable : transferred est vrai ssi le neutre devient attractif (\(\pi > 0.5\)) et le contrôle reste neutre (\(\pi < 0.05\)).
3. Le verdict de DISTINCTNESS — valence \(\neq\) prédiction
Le cœur falsifiable de #7740 : \(\pi\) (valence) et \(p̂\) (prédiction) doivent pouvoir diverger. Si la valence apprise n’était qu’un re-vêtement de la prédiction, l’expérience manquante serait vide.
Le banc valence_prediction_distinctness_test accepte un prédicteur sous deux arités :
predict_fn(i) — state-invariant : la prédiction ne lit pas la valence. Son erreur ne change pas entre avant et après conditionnement, donc \(\Delta_\mathrm{err} \approx 0\)\(\Rightarrow\)distinct = 1.
predict_fn(i, pi_t) — state-coupled : peut lire la valence. Un re-vêtement parfait p̂ = 1 - π_i voit son erreur suivre \(\pi\) (avant \(\pi{=}0 \Rightarrow\) err 1 ; après \(\pi{\approx}1 \Rightarrow\) err 0), donc \(\Delta_\mathrm{err} > 0\)\(\Rightarrow\)distinct = 0.
C’est ce passage du vecteur \(\pi_t\) au prédicteur qui rend le banc réellement réfutable (revue ai-01 #8823) : sans lui, le verdict était satisfait par construction pour toute fonction pure.
# Predicteur state-INVARIANT (1 arg) : l'erreur ne depend pas de la valence -> distinct.def predict_invariant(signal_idx: int) ->float:# Le signal 1 est mal predit (erreur 0.4), invariant au conditionnement.return0.4if signal_idx ==1else0.1# Predicteur state-COUPLED (2 args) : re-vetement p_hat = 1 - pi -> erreur suit pi -> NON distinct.def predict_coupled(signal_idx: int, pi_t) ->float:return1.0- pi_t[signal_idx]rep_inv = valence_prediction_distinctness_test( predict_fn=predict_invariant, n_signals=4, conditioned_idx=1, source_valence=1.0, n_condition=50, lr=0.1,)rep_coup = valence_prediction_distinctness_test( predict_fn=predict_coupled, n_signals=4, conditioned_idx=1, source_valence=1.0, n_condition=50, lr=0.1,)print(f"Invariant (1 arg) : delta_valence={rep_inv['delta_valence']:.3f} delta_pred_err={rep_inv['delta_prediction_error']:.4f} -> distinct={rep_inv['distinct']:.1f}")print(f"Couple (2 args) : delta_valence={rep_coup['delta_valence']:.3f} delta_pred_err={rep_coup['delta_prediction_error']:.4f} -> distinct={rep_coup['distinct']:.1f}")print("\nLe controle negatif (couple) prouve que le banc PEUT echouer : valence != prediction est desormais refutable.")
Invariant (1 arg) : delta_valence=0.995 delta_pred_err=0.0000 -> distinct=1.0
Couple (2 args) : delta_valence=0.995 delta_pred_err=0.9948 -> distinct=0.0
Le controle negatif (couple) prouve que le banc PEUT echouer : valence != prediction est desormais refutable.
4. Le verdict de RÉVERSIBILITÉ — acquisition \(\Leftrightarrow\) extinction
Une valence apprise doit pouvoir s’éteindre quand l’association est retirée (signal présenté seul). Une valence qui ne décroîtrait jamais serait un investissement figé — pathologique. Comme acquisition et extinction partagent la règle Rescorla-Wagner, présenter le signal seul ramène \(\pi\) vers 0.
Trois exercices pour rendre la valence apprise opératoire. Chaque stub s’exécute sans erreur (C.1) — complétez-le.
Exercice 1 — Conditionnement de second ordre
Le transfert #7740 est de premier ordre (source \(\to\) signal neutre). Le conditionnement pavlovien admet aussi le second ordre : un signal déjà attractif peut transférer sa valence à un autre signal neutre (signal B co-occurrent avec signal A déjà conditionné, sans la source). Implémentez ce protocole et mesurez si \(\pi_B\) monte (et de combien, vs le premier ordre).
Indice : conditionnez A avec la source, puis conditionnez B « via » A en passant source_valence=pi_A (la valence acquise de A devient la nouvelle source).
# Exercice 1 : conditionnement de second ordre.# TODO etudiant : implementer le protocole decrit ci-dessus.result =None# TODO etudiant : (pi_B_second_ordre, pi_B_premier_ordre) pour comparaisonprint("Exercice 1 a completer : mesurer pi_B en second ordre vs premier ordre.")if result isnotNone:print(f"second ordre = {result[0]:.3f}, premier ordre = {result[1]:.3f}")
Exercice 1 a completer : mesurer pi_B en second ordre vs premier ordre.
Exercice 2 — Seuil de taux d’apprentissage
Pour un budget fixé de n_condition présentations, il existe un taux d’apprentissage en dessous duquel le transfert échoue (la valence n’atteint pas 0.5). Trouvez ce seuil empiriquement pour n_condition=50 par dichotomie.
Indice : la convergence Rescorla-Wagner après \(n\) pas donne \(\pi \approx v_\text{source}(1-(1-\mathrm{lr})^n)\) ; résolvez pour le lr qui donne \(\pi = 0.5\), puis vérifiez avec valence_transfer_test.
# Exercice 2 : seuil de taux d'apprentissage pour n_condition=50.# TODO etudiant : trouver lr_seuil tel que transfer_test(lrn) bascule autour.lr_seuil =None# TODO etudiant : valeur empiriqueprint("Exercice 2 a completer : seuil de lr pour n_condition=50.")if lr_seuil isnotNone: r = valence_transfer_test(n_signals=4, neutral_idx=1, source_valence=1.0, n_condition=50, lr=lr_seuil)print(f"lr={lr_seuil:.4f} -> transferred={r['transferred']:.1f}")
Exercice 2 a completer : seuil de lr pour n_condition=50.
Exercice 3 — Un prédicteur subtilement couplé
Le banc de distinctness accepte un prédicteur predict_fn(i, pi_t). Construire un prédicteur partiellement couplé — qui lit la valence mais avec un poids \(\alpha \in (0,1)\) : \(p̂ = \alpha (1-\pi_i) + (1-\alpha) c_i\). Pour quel \(\alpha\) le verdict distinct bascule-t-il de 1 à 0 ? Cela quantifie combien de couplage il faut pour que valence et prédiction deviennent indistinguables.
Indice : le seuil du banc est \(|\Delta_\mathrm{err}| < 10^{-6}\). Calculez \(\Delta_\mathrm{err}(\alpha)\) analytiquement puis confirmez en balayant \(\alpha\).
# Exercice 3 : seuil de couplage alpha.# TODO etudiant : balayer alpha et trouver le bascule du verdict distinct.alpha_seuil =None# TODO etudiant : valeur de alpha ou distinct basculeprint("Exercice 3 a completer : seuil de couplage alpha pour distinctness.")if alpha_seuil isnotNone:print(f"alpha_seuil = {alpha_seuil}")
Exercice 3 a completer : seuil de couplage alpha pour distinctness.
Conclusion
La valence apprise complète la valence programmée d’ICT-12 sans s’y réduire : même formalisme (\(\pi_t\)), mais acquise par Rescorla-Wagner plutôt qu’inscrite, donc transférable (un neutre devient attractif par association) et réversible (extinction par la même règle). Et cruciallement distincte de la prédiction : le banc valence_prediction_distinctness_test rend ce verdict réfutable dans les deux sens — un prédicteur invariant satisfait distinct, un re-vêtement de \(\pi\) par \(p̂\) le fait basculer à 0.
C’est le grain #7740 (« l’expérience manquante ») : opérationnaliser l’investissement thomien — non pas où sera la proie (\(p̂\)), mais combien l’agent investit dans un signal (\(\pi\)). Le module ict.learned_valence est la brique ; ce notebook la met en scène. Les strates suivantes (argumentation #7289, jeu évolutif #7746) reprendront cette valence apprise comme source de prégnance transférable.