Better profile scoring weights

This commit is contained in:
Mr. Stallion 2021-03-21 16:28:13 -05:00
parent 0b5b005d91
commit fec16d1f89
5 changed files with 115 additions and 29 deletions

View File

@ -28,6 +28,11 @@
<search-history ref="searchHistory" :callback="updateSearch" :curSearch="data"></search-history>
</div>
<div v-else-if="state === 'results'" class="results">
<div class="debug" v-show="false">
<textarea v-model="debugSearchJson"></textarea>
<button class="btn" @click.prevent="debugUpdateResults()">Update</button>
</div>
<h4 v-if="hasReceivedResults">
{{results.length}} {{l('characterSearch.results')}}
@ -66,7 +71,14 @@
import {EventBus} from './preview/event-bus';
import CharacterSearchHistory from './CharacterSearchHistory.vue';
import { Matcher } from '../learn/matcher';
import { nonAnthroSpecies, Species, speciesMapping, speciesNames } from '../learn/matcher-types';
import {
kinkMatchScoreMap,
kinkMatchWeights,
nonAnthroSpecies,
Species,
speciesMapping,
speciesNames
} from '../learn/matcher-types';
import { CharacterCacheRecord } from '../learn/profile-cache';
import Bluebird from 'bluebird';
@ -140,6 +152,15 @@
state = 'search';
hasReceivedResults = false;
debugSearchJson = JSON.stringify(
{
scoreMap: kinkMatchScoreMap,
weights: kinkMatchWeights
},
null,
2
);
private countUpdater?: ResultCountUpdater;
data: ExtendedSearchData = {
@ -194,6 +215,39 @@
}
async debugUpdateResults(): Promise<void> {
if (this.state !== 'results') {
return;
}
const data = JSON.parse(this.debugSearchJson);
_.assign(kinkMatchScoreMap, data.scoreMap);
_.assign(kinkMatchWeights, data.weights);
core.cache.profileCache.clear();
const results = this.results;
this.results = [];
await Bluebird.delay(10);
// pre-warm cache
await Bluebird.mapSeries(
results,
(c) => core.cache.profileCache.get(c.character.name)
);
this.resultsPending = this.countPendingResults(undefined, results);
this.countUpdater?.start();
this.resort(results);
console.log('Done!');
}
@Hook('mounted')
mounted(): void {
core.connection.onMessage('ERR', (data) => {

View File

@ -6,9 +6,10 @@
import { Component, Hook, Prop, Watch } from '@f-list/vue-ts';
import Vue from 'vue';
import {Channel, Character} from '../fchat';
import { Matcher, Score, Scoring } from '../learn/matcher';
import { Score, Scoring } from '../learn/matcher';
import core from './core';
import { EventBus } from './preview/event-bus';
import { kinkMatchWeights } from '../learn/matcher-types';
export function getStatusIcon(status: Character.Status): string {
@ -71,7 +72,7 @@ export function getStatusClasses(
const cache = core.cache.profileCache.getSync(character.name);
if (cache) {
if ((cache.match.searchScore > Matcher.UNICORN_LEVEL) && (cache.match.matchScore === Scoring.MATCH)) {
if ((cache.match.searchScore > kinkMatchWeights.unicornThreshold) && (cache.match.matchScore === Scoring.MATCH)) {
matchClass = 'match-found unicorn';
matchScore = 'unicorn';
} else {

View File

@ -11,4 +11,8 @@ export abstract class AsyncCache<RecordType> {
static nameKey(name: string): string {
return Cache.nameKey(name);
}
clear(): void {
this.cache = {};
}
}

View File

@ -484,32 +484,39 @@ export interface SpeciesMappingCache {
}
export const kinkMatchWeights = {
logBase: 10,
weakMismatchThreshold: 0.3,
weakMatchThreshold: 0.3,
unicornThreshold: 8.0
};
export const kinkMatchScoreMap = {
favorite: {
favorite: 0.55,
favorite: 0.5,
yes: 0.25,
maybe: -0.5,
no: -1.0
no: -2
},
yes: {
favorite: 0.20,
yes: 0.20,
maybe: -0.25,
favorite: 0.3,
yes: 0.2,
maybe: -0.15,
no: -0.5
},
maybe: {
favorite: -0.5,
yes: -0.25,
yes: -0.2,
maybe: 0,
no: 0
},
no: {
favorite: -1.0,
favorite: -2,
yes: -0.5,
maybe: -0.1,
maybe: 0,
no: 0
}
};

View File

@ -15,7 +15,7 @@ import {
FurryPreference,
Gender, genderKinkMapping,
Kink, KinkBucketScore, kinkComparisonExclusionGroups, kinkComparisonExclusions, kinkComparisonSwaps,
kinkMapping, kinkMatchScoreMap,
kinkMapping, kinkMatchScoreMap, kinkMatchWeights,
KinkPreference, likelyHuman, mammalSpecies, nonAnthroSpecies,
Orientation,
Species, SpeciesMap, speciesMapping, SpeciesMappingCache,
@ -180,9 +180,6 @@ export class Matcher {
readonly yourAnalysis: CharacterAnalysis;
readonly theirAnalysis: CharacterAnalysis;
static readonly UNICORN_LEVEL = 6.0;
constructor(you: Character, them: Character, yourAnalysis?: CharacterAnalysis, theirAnalysis?: CharacterAnalysis) {
this.you = you;
this.them = them;
@ -198,8 +195,8 @@ export class Matcher {
const youThem = new Matcher(you, them, yourAnalysis, theirAnalysis);
const themYou = new Matcher(them, you, theirAnalysis, yourAnalysis);
const youThemMatch = youThem.match();
const themYouMatch = themYou.match();
const youThemMatch = youThem.match('their');
const themYouMatch = themYou.match('your');
const report: MatchReport = {
_isVue: true,
@ -240,8 +237,8 @@ export class Matcher {
const youThem = new Matcher(yourAnalysis.character, theirAnalysis.character, yourAnalysis.analysis, theirAnalysis.analysis);
const themYou = new Matcher(theirAnalysis.character, yourAnalysis.character, theirAnalysis.analysis, yourAnalysis.analysis);
const youThemMatch = youThem.match();
const themYouMatch = themYou.match();
const youThemMatch = youThem.match('their');
const themYouMatch = themYou.match('your');
const report: MatchReport = {
_isVue: true,
@ -377,7 +374,7 @@ export class Matcher {
return (finalScore === null) ? Scoring.NEUTRAL : finalScore;
}
match(): MatchResult {
match(pronoun: string): MatchResult {
const data: MatchResult = {
you: this.you,
them: this.them,
@ -394,7 +391,7 @@ export class Matcher {
[TagId.FurryPreference]: this.resolveFurryPairingsScore(),
[TagId.Species]: this.resolveSpeciesScore(),
[TagId.SubDomRole]: this.resolveSubDomScore(),
[TagId.Kinks]: this.resolveKinkScore()
[TagId.Kinks]: this.resolveKinkScore(pronoun)
},
info: {
@ -570,7 +567,7 @@ export class Matcher {
}
private resolveKinkScore(): Score {
private resolveKinkScore(pronoun: string): Score {
const kinkScore = this.resolveKinkBucketScore('all');
log.debug('report.score.kink', this.them.name, this.you.name, kinkScore.count, kinkScore.score, kinkScore.weighted);
@ -580,18 +577,18 @@ export class Matcher {
}
if (kinkScore.weighted < 0) {
if (Math.abs(kinkScore.weighted) < 0.45) {
return new Score(Scoring.WEAK_MISMATCH, 'Challenging <span>kinks</span>');
if (Math.abs(kinkScore.weighted) < kinkMatchWeights.weakMismatchThreshold) {
return new Score(Scoring.WEAK_MISMATCH, `Hesitant about ${pronoun} <span>kinks</span>`);
}
return new Score(Scoring.MISMATCH, 'Mismatching <span>kinks</span>');
return new Score(Scoring.MISMATCH, `Dislikes ${pronoun} <span>kinks</span>`);
}
if (Math.abs(kinkScore.weighted) < 0.45) {
return new Score(Scoring.WEAK_MATCH, 'Good <span>kinks</span>');
if (Math.abs(kinkScore.weighted) < kinkMatchWeights.weakMatchThreshold) {
return new Score(Scoring.WEAK_MATCH, `Likes ${pronoun} <span>kinks</span>`);
}
return new Score(Scoring.MATCH, 'Great <span>kinks</span>');
return new Score(Scoring.MATCH, `Loves ${pronoun} <span>kinks</span>`);
}
@ -789,6 +786,8 @@ export class Matcher {
const yourKinks = this.getAllStandardKinks(this.you);
const theirKinks = this.getAllStandardKinks(this.them);
// let missed = 0;
const result: any = _.reduce(
yourKinks,
(accum, yourKinkValue: any, yourKinkId: any) => {
@ -816,15 +815,19 @@ export class Matcher {
};
}
// missed += 1;
return accum;
},
{ score: 0, count: 0 }
);
// const yourBucketCounts = this.countKinksByBucket(yourKinks);
// const theirBucketCounts = this.countKinksByBucket(theirKinks);
result.weighted = (result.count === 0)
? 0
: (
(Math.log(result.count) / Math.log(5)) // log 5 base
(Math.log(result.count) / Math.log(kinkMatchWeights.logBase)) // log 8 base
* (result.score / result.count)
);
@ -832,6 +835,23 @@ export class Matcher {
}
// private countKinksByBucket(kinks: { [key: number]: KinkChoice }): { favorite: number, yes: number, maybe: number, no: number } {
// return _.reduce(
// kinks,
// (accum, kinkValue) => {
// accum[kinkValue] += 1;
// return accum;
// },
// {
// favorite: 0,
// yes: 0,
// maybe: 0,
// no: 0
// }
// );
// }
private getAllStandardKinks(c: Character): { [key: number]: KinkChoice } {
const kinks = _.pickBy(c.kinks, _.isString);