Aufgabe
Implementieren Sie ein Programm in minimalen Byte Quell- oder Binärcode, das die Spracherkennung eines Sprachmusters (wobei ich "Ja", "Ja" oder "Nein" in der Stimme oder im Flüsterton, einfach oder schrullig, sage) basierend auf Trainingsmustern mit maximaler Genauigkeit ausführt .
Das Programm sollte lesen train/yes0.wav
, train/no0.wav
, train/yes1.wav
und so weiter (es gibt 400 Ja und 400 Neins in Trainingsdaten), dann anfangen zu lesen inputs/0.wav
, inputs/1.wav
bis es die Datei nicht finden kann, es zu analysieren und „ja“ oder „nein“ (oder ein anderes Wort ausgibt für Zwischenantwort).
Wenn Sie möchten, können Sie das Programm vorab trainieren, anstatt es zu lesen train/
. Die resultierende Datentabelle wird jedoch zur Punktzahl hinzugerechnet (und achten Sie darauf, dass die Trainingsmuster nicht zu stark angepasst werden - sie überschneiden sich nicht mit den Prüfungsproben). In diesem Fall ist es besser, das zur Erstellung der Datentabelle verwendete Programm als Nachtrag aufzunehmen.
Alle Beispieldateien sind Little-Endian-16-Bit-Stereo-WAV-Dateien, nur vom Laptop-Mikrofon, ohne Filterung / Rauschunterdrückung.
Grenzen
Verbotene Funktionen:
- Netzwerk verwenden;
- Der Versuch, die Antwortdatei zu erreichen
inputs/key
; - Untergraben des
runner
Programms, das die Genauigkeit berechnet; - Vorhandene Erkennungsbibliotheken verwenden. Die Verknüpfung mit einer FFT-Implementierung ist nicht zulässig: Es sind nur externe mathematische Funktionen zulässig, die eine konstante Menge an Informationen (wie
sin
oderatan2
) enthalten. Wenn Sie FFT möchten, fügen Sie die Implementierung einfach Ihrem Programmquellcode hinzu (sie kann bei Bedarf mehrsprachig sein).
Ressourcenlimits:
- Das Programm sollte auf meinem i5-Laptop nicht länger als 30 Minuten CPU-Zeit beanspruchen. Wenn es länger dauert, werden nur die in den ersten 30 Minuten produzierten Ergebnisse gezählt, und für alle anderen wird eine halbe Übereinstimmung angenommen.
- Speicherlimit: 1 GB (einschließlich temporärer Dateien);
Werkzeuge
Das tools/runner
Programm führt Ihre Lösung automatisch aus und berechnet die Genauigkeit.
$ tools/runner solutions/example train train/key
Accuracy: 548 ‰
Es kann das Programm anhand von Trainingsdaten oder anhand von tatsächlichen Prüfungsdaten untersuchen. Ich werde eingereichte Antworten auf den Untersuchungsdatensatz versuchen und die Ergebnisse veröffentlichen (Genauigkeit in Prozent), bis ich den Datensatz öffentlich mache.
Wertung
Je nach Genauigkeit gibt es 5 Lösungsklassen:
- Alle Proben richtig geraten: Klasse 0;
- Genauigkeit 950-999: Klasse 1;
- Genauigkeit 835-950: Klasse 2;
- Genauigkeit 720-834: Klasse 3;
- Genauigkeit 615-719: Klasse 4;
In jeder Klasse gibt die Bewertung die Anzahl der Bytes an, die die Lösung benötigt.
Akzeptierte Antwort: Kleinste Lösung in der besten nicht leeren Klasse.
Links
- Github-Projekt mit Tools: https://github.com/vi/codegolf-jein
- Trainingsdatensatz: http://vi-server.org/pub/codegolf-jein-train.tar.xz
- Der Untersuchungsdatensatz wird so weit privat gehalten, dass Prüfsummen (HMAC) im Github-Repository verfügbar sind.
Alle Beispiele sollten als CC-0 (Public Domain) betrachtet werden, Skripte und Programme sollten als MIT betrachtet werden.
Beispiellösung
Es bietet eine sehr schlechte Erkennungsqualität und zeigt lediglich, wie Dateien gelesen und Antworten ausgegeben werden
#define _BSD_SOURCE
#include <stdio.h>
#include <assert.h>
#include <endian.h>
#define Nvols 30
#define BASS_WINDOW 60
#define MID_WINDOW 4
struct training_info {
double bass_volumes[Nvols];
double mid_volumes[Nvols];
double treble_volumes[Nvols];
int n;
};
struct training_info yes;
struct training_info no;
static int __attribute__((const)) mod(int n, int d) {
int m = n % d;
if (m < 0) m+=d;
return m;
}
// harccoded to 2 channel s16le
int get_file_info(const char* name, struct training_info *inf) {
FILE* in = fopen(name, "rb");
if (!in) return -1;
setvbuf(in, NULL, _IOFBF, 65536);
inf->n = 1;
fseek(in, 0, SEEK_END);
long filesize = ftell(in);
fseek(in, 128, SEEK_SET);
filesize -= 128; // exclude header and some initial samples
int nsamples = filesize / 4;
double bass_a=0, mid_a=0;
const int HISTSIZE = 101;
double xhistory[HISTSIZE];
int histpointer=0;
int histsize = 0;
//FILE* out = fopen("debug.raw", "wb");
int i;
for (i=0; i<Nvols; ++i) {
int j;
double total_vol = 0;
double bass_vol = 0;
double mid_vol = 0;
double treble_vol = 0;
for (j=0; j<nsamples / Nvols; ++j) {
signed short int l, r; // a sample
if(fread(&l, 2, 1, in)!=1) break;
if(fread(&r, 2, 1, in)!=1) break;
double x = 1/65536.0 * ( le16toh(l) + le16toh(r) );
bass_a += x;
mid_a += x;
if (histsize == HISTSIZE-1) bass_a -= xhistory[mod(histpointer-BASS_WINDOW,HISTSIZE)];
if (histsize == HISTSIZE-1) mid_a -= xhistory[mod(histpointer-MID_WINDOW ,HISTSIZE)];
double bass = bass_a / BASS_WINDOW;
double mid = mid_a / MID_WINDOW - bass;
double treble = x - mid_a/MID_WINDOW;
xhistory[histpointer++] = x;
if(histpointer>=HISTSIZE) histpointer=0;
if(histsize < HISTSIZE-1) ++histsize;
total_vol += bass*bass + mid*mid + treble*treble;
bass_vol += bass*bass;
mid_vol += mid*mid;
treble_vol += treble*treble;
/*
signed short int y;
y = 65536 * bass;
y = htole16(y);
fwrite(&y, 2, 1, out);
fwrite(&y, 2, 1, out);
*/
}
inf->bass_volumes[i] = bass_vol / total_vol;
inf->mid_volumes[i] = mid_vol / total_vol;
inf->treble_volumes[i] = treble_vol / total_vol;
//fprintf(stderr, "%lf %lf %lf %s\n", inf->bass_volumes[i], inf->mid_volumes[i], inf->treble_volumes[i], name);
}
fclose(in);
return 0;
}
static void zerotrdata(struct training_info *inf) {
int i;
inf->n = 0;
for (i=0; i<Nvols; ++i) {
inf->bass_volumes[i] = 0;
inf->mid_volumes[i] = 0;
inf->treble_volumes[i] = 0;
}
}
static void train1(const char* prefix, struct training_info *inf)
{
char buf[50];
int i;
for(i=0;; ++i) {
sprintf(buf, "%s%d.wav", prefix, i);
struct training_info ti;
if(get_file_info(buf, &ti)) break;
++inf->n;
int j;
for (j=0; j<Nvols; ++j) {
inf->bass_volumes[j] += ti.bass_volumes[j];
inf->mid_volumes[j] += ti.mid_volumes[j];
inf->treble_volumes[j] += ti.treble_volumes[j];
}
}
int j;
for (j=0; j<Nvols; ++j) {
inf->bass_volumes[j] /= inf->n;
inf->mid_volumes[j] /= inf->n;
inf->treble_volumes[j] /= inf->n;
}
}
static void print_part(struct training_info *inf, FILE* f) {
fprintf(f, "%d\n", inf->n);
int j;
for (j=0; j<Nvols; ++j) {
fprintf(f, "%lf %lf %lf\n", inf->bass_volumes[j], inf->mid_volumes[j], inf->treble_volumes[j]);
}
}
static void train() {
zerotrdata(&yes);
zerotrdata(&no);
fprintf(stderr, "Training...\n");
train1("train/yes", &yes);
train1("train/no", &no);
fprintf(stderr, "Training completed.\n");
//print_part(&yes, stderr);
//print_part(&no, stderr);
int j;
for (j=0; j<Nvols; ++j) {
if (yes.bass_volumes[j] > no.bass_volumes[j]) { yes.bass_volumes[j] = 1; no.bass_volumes[j] = 0; }
if (yes.mid_volumes[j] > no.mid_volumes[j]) { yes.mid_volumes[j] = 1; no.mid_volumes[j] = 0; }
if (yes.treble_volumes[j] > no.treble_volumes[j]) { yes.treble_volumes[j] = 1; no.treble_volumes[j] = 0; }
}
}
double delta(struct training_info *t1, struct training_info *t2) {
int j;
double d = 0;
for (j=0; j<Nvols; ++j) {
double rb = t1->bass_volumes[j] - t2->bass_volumes[j];
double rm = t1->mid_volumes[j] - t2->mid_volumes[j];
double rt = t1->treble_volumes[j] - t2->treble_volumes[j];
d += rb*rb + rm*rm + rt*rt;
}
return d;
}
int main(int argc, char* argv[])
{
(void)argc; (void)argv;
train();
int i;
int yes_count = 0;
int no_count = 0;
for (i=0;; ++i) {
char buf[60];
sprintf(buf, "inputs/%d.wav", i);
struct training_info ti;
if(get_file_info(buf, &ti)) break;
double dyes = delta(&yes, &ti);
double dno = delta(&no, &ti);
//printf("%lf %lf %s ", dyes, dno, buf);
if (dyes > dno) {
printf("no\n");
++no_count;
} else {
printf("yes\n");
++yes_count;
}
}
fprintf(stderr, "yeses: %d noes: %d\n", yes_count, no_count);
}
sum
oder müssen wir verwendenfoldl (+) 0
(foldl ist nicht mathematikspezifisch und+
nicht variadisch)?sum
. Ich denke, das ist nicht deine Absicht?Antworten:
C ++ 11 (gcc;
163916251635 Bytes, Klasse 1, Score = 983, 960)Fangen wir an. Es ist wahrscheinlich der längste Code, den ich je gekürzt habe ...
"Ungolfed" (obwohl es schwierig ist, einen über 1,5 KB großen Quellcode als Golf zu bezeichnen):
Ich habe keine Ahnung, ob es in einem echten Datensatz richtig funktioniert (ich wette, das wird es nicht, aber ich muss es versuchen).
Wie es funktioniert:
log(mean distribution)+10
und dann normalisiert, so dass die Summe der größten Peaks 1 war.dunno
.Wie ich bereits sagte, wird es wahrscheinlich bei den letzten Tests als "noch schlimmer als zufällig" eingestuft. Natürlich hoffe ich nicht: D
Bearbeiten: Fehler behoben (vergessen, die Dateien zu schließen).
quelle
worse than random
. Sie müssen buchstäblich nur ein Byte ändern -distYes > distNo
und das wird es auch tunbetter than random
. Oder anders ausgedrückt, es wäre ziemlich erstaunlich, wenn Sie das Ergebnis eines Münzwurfs 100 Mal hintereinander falsch erraten könnten! Und es ist keine Seltenheit, dass diese einfachen Algorithmen besser abschneiden als komplexere. +1 und ich wünsche Ihnen viel Glück.EMFILE (Too many open files)
... versucht wird, das Problem zu beheben ...Accuracy: 983 ‰; Time: 0m27.570s;
; Prüfung Dataset:Accuracy: 960 ‰; Time: 0m32.957s
. Gut gemacht.#define
s: P