dewmal commited on
Commit
0768e31
·
verified ·
1 Parent(s): 4596159

Upload Concept Memory base model

Browse files
Files changed (2) hide show
  1. README.md +76 -0
  2. concept_memory/concept_bank.py +82 -0
README.md CHANGED
@@ -34,6 +34,15 @@ The three supported judgment types are:
34
  lowest to highest.
35
  - **Noul (Yes/No):** return a binary judgment for the instruction.
36
 
 
 
 
 
 
 
 
 
 
37
  The example below demonstrates Choice. The same `Question` interface supports
38
  the other judgment types by setting `type` to `"score"` or `"noul"` and
39
  providing the corresponding criteria where applicable.
@@ -109,6 +118,73 @@ print("Prediction:", predicted.id, "-", predicted.description)
109
  print("Probabilities:", result.probabilities.tolist())
110
  ```
111
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
  The Hub checkpoint is a PyTorch artifact for this project's Python package; it
113
  is not directly compatible with `transformers.AutoModel.from_pretrained`.
114
 
 
34
  lowest to highest.
35
  - **Noul (Yes/No):** return a binary judgment for the instruction.
36
 
37
+ The idea behind **Concept Memory** is to make a judgment through reusable,
38
+ human-readable concepts. For example, when routing a support request, concepts
39
+ such as “payment issue” or “integration failure” can connect details in the
40
+ input to a possible outcome. The base checkpoint makes the typed judgment and
41
+ can rank which concepts are relevant; a Concept Bank adds explicit links from
42
+ those concepts to outcomes. This makes the reasoning path inspectable and lets
43
+ you adapt the bank to your domain. A relevance score is a relative ranking, not
44
+ a calibrated measure of evidence.
45
+
46
  The example below demonstrates Choice. The same `Question` interface supports
47
  the other judgment types by setting `type` to `"score"` or `"noul"` and
48
  providing the corresponding criteria where applicable.
 
118
  print("Probabilities:", result.probabilities.tolist())
119
  ```
120
 
121
+ ## Use a Concept Bank
122
+
123
+ This example builds a small bank of domain concepts. Each concept describes a
124
+ pattern and names the outcome it supports or contradicts. The model first
125
+ ranks relevant concepts, then the helper blends their linked votes with the
126
+ raw Choice probabilities only when the top bank outcome agrees with the raw
127
+ model prediction. The helper is included in the repository's minimal runtime
128
+ package.
129
+
130
+ ```python
131
+ from concept_memory.concept_bank import apply_concept_votes
132
+
133
+ concepts = [
134
+ {
135
+ "id": "payment_issue",
136
+ "name": "Payment issue",
137
+ "description": "A charge, invoice, refund, or subscription problem.",
138
+ "supports": ["billing"],
139
+ },
140
+ {
141
+ "id": "integration_failure",
142
+ "name": "Integration failure",
143
+ "description": "An API, SDK, or third-party integration is failing.",
144
+ "supports": ["technical"],
145
+ },
146
+ {
147
+ "id": "purchase_intent",
148
+ "name": "Purchase intent",
149
+ "description": "The customer is evaluating plans or pricing before buying.",
150
+ "supports": ["sales"],
151
+ },
152
+ ]
153
+
154
+ concept_question = Question(
155
+ id="concepts",
156
+ type="choice",
157
+ instructions=f"Which concepts are most relevant to this input and question: {question.instructions}",
158
+ criteria=[
159
+ {"id": item["id"], "description": f"{item['name']}: {item['description']}"}
160
+ for item in concepts
161
+ ],
162
+ )
163
+
164
+ with torch.inference_mode():
165
+ raw_result, concept_result = model.predict(
166
+ "Stripe integration keeps failing after several attempts.",
167
+ [question, concept_question],
168
+ )
169
+
170
+ outcomes = [
171
+ {"id": item.id, "description": item.description}
172
+ for item in question.normalized_criteria()
173
+ ]
174
+ signals = [
175
+ {"description": concept["name"], "probability": float(probability)}
176
+ for concept, probability in zip(concepts, concept_result.probabilities.tolist())
177
+ ]
178
+ adjusted_probabilities, contributions = apply_concept_votes(
179
+ raw_result.probabilities.tolist(), signals, concepts, outcomes
180
+ )
181
+
182
+ winner = max(range(len(adjusted_probabilities)), key=adjusted_probabilities.__getitem__)
183
+ print("Concept-assisted prediction:", outcomes[winner]["description"])
184
+ print("Adjusted probabilities:", adjusted_probabilities)
185
+ print("Concept contributions:", contributions)
186
+ ```
187
+
188
  The Hub checkpoint is a PyTorch artifact for this project's Python package; it
189
  is not directly compatible with `transformers.AutoModel.from_pretrained`.
190
 
concept_memory/concept_bank.py ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Concept Bank vote blending shared by the playground and benchmark."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ CONCEPT_BANK_POLICY_VERSION = "agreement-gate-v2"
8
+
9
+
10
+ def _resolve_outcome(value: Any, outcomes: list[dict[str, str]]) -> int | None:
11
+ target = str(value).strip().casefold()
12
+ for index, outcome in enumerate(outcomes):
13
+ if target in {outcome["id"].casefold(), outcome["description"].casefold()}:
14
+ return index
15
+ aliases = {"true": "yes", "false": "no", "1": "yes", "0": "no"}
16
+ target = aliases.get(target, target)
17
+ for index, outcome in enumerate(outcomes):
18
+ if target in {outcome["id"].casefold(), outcome["description"].casefold()}:
19
+ return index
20
+ return None
21
+
22
+
23
+ def apply_concept_votes(
24
+ raw_probabilities: list[float],
25
+ signals: list[dict[str, Any]],
26
+ concepts: list[dict[str, Any]],
27
+ outcomes: list[dict[str, str]],
28
+ ) -> tuple[list[float], list[dict[str, Any]]]:
29
+ """Use linked bank votes only when they agree with the raw decision.
30
+
31
+ Relevance is a relative ranking, not a calibrated measure of evidence. Until
32
+ bank reliability is measured, a conflicting vote must not override the model.
33
+ """
34
+ votes = [0.0] * len(outcomes)
35
+ contributions = []
36
+ for signal, concept in zip(signals, concepts):
37
+ relevance = float(signal["probability"])
38
+ supports = concept.get("supports", []) or []
39
+ contradicts = concept.get("contradicts", []) or []
40
+ if isinstance(supports, str):
41
+ supports = [supports]
42
+ if isinstance(contradicts, str):
43
+ contradicts = [contradicts]
44
+ supported = sorted(
45
+ {i for value in supports if (i := _resolve_outcome(value, outcomes)) is not None}
46
+ )
47
+ opposed = sorted(
48
+ {i for value in contradicts if (i := _resolve_outcome(value, outcomes)) is not None}
49
+ )
50
+ concept_votes = [0.0] * len(outcomes)
51
+ if supported:
52
+ for i in supported:
53
+ concept_votes[i] += relevance / len(supported)
54
+ if opposed:
55
+ alternatives = [i for i in range(len(outcomes)) if i not in opposed]
56
+ if alternatives:
57
+ for i in alternatives:
58
+ concept_votes[i] += relevance / len(alternatives)
59
+ for i, amount in enumerate(concept_votes):
60
+ votes[i] += amount
61
+ contributions.append(
62
+ {
63
+ "description": signal["description"],
64
+ "probability": relevance,
65
+ "votes": [
66
+ {"description": outcomes[i]["description"], "weight": amount}
67
+ for i, amount in enumerate(concept_votes)
68
+ if amount > 0
69
+ ],
70
+ }
71
+ )
72
+ total = sum(votes)
73
+ if total <= 0:
74
+ return raw_probabilities, []
75
+ bank_probabilities = [value / total for value in votes]
76
+ raw_winner = max(range(len(raw_probabilities)), key=raw_probabilities.__getitem__)
77
+ bank_winner = max(range(len(bank_probabilities)), key=bank_probabilities.__getitem__)
78
+ if bank_winner != raw_winner:
79
+ return raw_probabilities, contributions
80
+ adjusted = [0.2 * raw + 0.8 * bank for raw, bank in zip(raw_probabilities, bank_probabilities)]
81
+ adjusted_total = sum(adjusted)
82
+ return [value / adjusted_total for value in adjusted], contributions