oracular 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/README.md +3 -2
- package/dist/cli.js +7 -3
- package/dist/index.cjs +6 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +6 -5
- package/dist/index.js.map +1 -1
- package/dist/presets.cjs +2 -2
- package/dist/presets.cjs.map +1 -1
- package/dist/presets.js +2 -2
- package/dist/presets.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.11.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#58](https://github.com/Yuribenjamin/oracular/pull/58) [`d6d1e87`](https://github.com/Yuribenjamin/oracular/commit/d6d1e87e176fc0112384e47fb4782e12d6fa1d1a) Thanks [@Yuribenjamin](https://github.com/Yuribenjamin)! - **Learning no longer drifts without outcomes.** The outcome rates a signal is judged by, with it and without it, were Laplace-smoothed, which centres both on 0.5 with one pseudo-hit each: two groups with no outcome at all then differ only by their sizes, so with zero outcomes a common signal drifted negative and a rare one positive, and a weight could change sign on no evidence (a weight of 2 seen in 90 of 100 outcome-free episodes ended at −0.59). Both rates are now shrunk toward the intent's own base rate by two pseudo-episodes, `(hits + 2·q₀) / (n + 2)` with `q₀ = outcomes / episodes`: a signal whose outcome rate equals the base rate learns exactly zero whatever the sample sizes, with no outcome every rate is 0 and no weight moves away from the developer's, and a weight never changes sign without outcomes on at least one side. The association-rule baseline becomes the base rate itself. `tune` fits with the same smoothing, so its weights, intervals and lifts move a little on small samples (a signal seen once with the outcome over a low base rate is worth a little less than before); on large samples the change is in the third decimal. Every learned number in the documentation's generated tables was regenerated. The state format is unchanged, and existing learned counts are kept: only the weight read from them moves.
|
|
8
|
+
|
|
3
9
|
## 0.10.0
|
|
4
10
|
|
|
5
11
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -193,8 +193,9 @@ A signal's weight is then a blend of your prior and the learned estimate:
|
|
|
193
193
|
```
|
|
194
194
|
nWith = hits + misses episodes with the signal
|
|
195
195
|
nWithout = episodes − nWith episodes without it
|
|
196
|
-
|
|
197
|
-
|
|
196
|
+
q₀ = outcomes / episodes the intent's own base rate
|
|
197
|
+
qWith = (hits + 2·q₀) / (nWith + 2) outcome rate with the signal
|
|
198
|
+
qWithout = (outcomes − hits + 2·q₀) / (nWithout + 2) outcome rate without it
|
|
198
199
|
lift = (qWith − qWithout) / (1 − qWithout) if qWith ≥ qWithout, else (qWith − qWithout) / qWithout → in [−1, 1]
|
|
199
200
|
learned = gain × |w₀| × lift 0 until the signal has history
|
|
200
201
|
nEff = (nWith × nWithout) / (nWith + nWithout) 0 if either side is 0
|
package/dist/cli.js
CHANGED
|
@@ -437,6 +437,9 @@ import {
|
|
|
437
437
|
function laplace(successes, total) {
|
|
438
438
|
return (successes + 1) / (total + 2);
|
|
439
439
|
}
|
|
440
|
+
function smoothRate(successes, total, rate) {
|
|
441
|
+
return (successes + 2 * rate) / (total + 2);
|
|
442
|
+
}
|
|
440
443
|
function lift(q, b, type = "normalized-difference") {
|
|
441
444
|
if (type === "absolute-difference") return q - b;
|
|
442
445
|
if (type !== "normalized-difference") {
|
|
@@ -528,9 +531,10 @@ function fitSignal(signal, current, tally, strength, priorEpisodes, liftType) {
|
|
|
528
531
|
const nWith = hits + misses;
|
|
529
532
|
const without = Math.max(0, tally.episodes - nWith);
|
|
530
533
|
const outcomesWithout = Math.min(without, Math.max(0, tally.outcomes - hits));
|
|
531
|
-
const
|
|
532
|
-
const
|
|
533
|
-
const
|
|
534
|
+
const base = tally.episodes === 0 ? 0 : tally.outcomes / tally.episodes;
|
|
535
|
+
const qWith = smoothRate(hits, nWith, base);
|
|
536
|
+
const qWithout = smoothRate(outcomesWithout, without, base);
|
|
537
|
+
const baseline = liftType === "association-rule" ? smoothRate(hits + outcomesWithout, nWith + without, base) : qWithout;
|
|
534
538
|
const noContrast = without === 0;
|
|
535
539
|
const rawLift = noContrast ? null : nWith === 0 ? 0 : lift(qWith, baseline, liftType);
|
|
536
540
|
const score = rawLift === null ? null : nWith === 0 ? 0 : learningScore(rawLift, liftType);
|
package/dist/index.cjs
CHANGED
|
@@ -79,8 +79,8 @@ function lowerBound(alpha, beta, z, bound) {
|
|
|
79
79
|
function repeatFactor(n) {
|
|
80
80
|
return Math.log1p(1 / n) / Math.LN2;
|
|
81
81
|
}
|
|
82
|
-
function
|
|
83
|
-
return (successes +
|
|
82
|
+
function smoothRate(successes, total, rate) {
|
|
83
|
+
return (successes + 2 * rate) / (total + 2);
|
|
84
84
|
}
|
|
85
85
|
function lift(q, b, type = "normalized-difference") {
|
|
86
86
|
if (type === "absolute-difference") return q - b;
|
|
@@ -154,12 +154,13 @@ function signalWeights(state, intent, learning, signal) {
|
|
|
154
154
|
}
|
|
155
155
|
const record2 = getOwn(state.learned, signal) ?? NONE;
|
|
156
156
|
const nWith = record2.hits + record2.misses;
|
|
157
|
-
const qWith = laplace(record2.hits, nWith);
|
|
158
157
|
const totalEpisodes = state.episodes.hits + state.episodes.misses;
|
|
159
158
|
const nWithout = Math.max(0, totalEpisodes - nWith);
|
|
160
159
|
const outcomesWithout = Math.min(nWithout, Math.max(0, state.episodes.hits - record2.hits));
|
|
161
|
-
const
|
|
162
|
-
const
|
|
160
|
+
const base = totalEpisodes === 0 ? 0 : state.episodes.hits / totalEpisodes;
|
|
161
|
+
const qWith = smoothRate(record2.hits, nWith, base);
|
|
162
|
+
const qWithout = smoothRate(outcomesWithout, nWithout, base);
|
|
163
|
+
const baseline = learning.lift === "association-rule" ? smoothRate(record2.hits + outcomesWithout, nWith + nWithout, base) : qWithout;
|
|
163
164
|
const score = learningScore(lift(qWith, baseline, learning.lift), learning.lift);
|
|
164
165
|
const learned = nWith === 0 ? 0 : learning.gain * Math.abs(initial) * score;
|
|
165
166
|
const nEff = nWith + nWithout === 0 ? 0 : nWith * nWithout / (nWith + nWithout);
|