Tâche
Mettre en œuvre un programme en octets minimum de code source ou binaire qui fait la reconnaissance vocale d'un échantillon vocal (moi en disant "oui", "oui" ou "non" en voix ou en chuchotement, clairement ou bizarrement) basé sur des échantillons d'apprentissage avec une précision maximale .
Le programme devrait lire train/yes0.wav
, train/no0.wav
, train/yes1.wav
et ainsi de suite (il y a 400 ouis et 400 NoE dans les données de formation), puis commencer à lire inputs/0.wav
, inputs/1.wav
jusqu'à ce qu'il ne trouve pas le fichier, l' analyse et la sortie « oui » ou « non » (ou tout autre mot pour réponse intermédiaire).
Si vous le souhaitez, vous pouvez pré-former le programme au lieu de lire train/
, mais le tableau de données résultant compte pour le score (et méfiez-vous du surajustement des échantillons de formation - ils ne chevauchent pas ceux de l'examen). Mieux vaut inclure le programme utilisé pour produire le tableau de données comme addendum dans ce cas.
Tous les exemples de fichiers sont de petits fichiers WAV stéréo 16 bits endian, uniquement à partir du micro d'un ordinateur portable, sans filtrage / réduction du bruit.
Limites
Fonctionnalités interdites:
- Utilisation du réseau;
- Essayer d'accéder au fichier de réponses
inputs/key
; - Détournement du
runner
programme qui calcule la précision; - Utilisation des bibliothèques de reconnaissance existantes. La liaison avec l'implémentation FFT n'est pas autorisée: seules les fonctions mathématiques externes prenant une quantité constante d'informations (comme
sin
ouatan2
) sont autorisées; Si vous voulez la FFT, ajoutez simplement son implémentation au code source de votre programme (il peut être multilingue si nécessaire).
Limites des ressources:
- Le programme ne devrait pas prendre plus de 30 minutes de temps processeur sur mon ordinateur portable i5. Si cela prend plus, seule la sortie produite dans les 30 premières minutes est comptée et tout le reste est supposé une demi-correspondance;
- Limite de mémoire: 1 Go (y compris tous les fichiers temporaires);
Outils
Le tools/runner
programme exécute automatiquement votre solution et calcule la précision.
$ tools/runner solutions/example train train/key
Accuracy: 548 ‰
Il peut examiner le programme à l'aide de données de formation ou à l'aide de données d'examen réelles. Je vais essayer de soumettre les réponses sur l'ensemble de données d'examen et publier les résultats (pourcentage de précision) jusqu'à ce que je rende l'ensemble de données public.
Notation
Il existe 5 classes de solutions selon la précision:
- Tous les échantillons ont deviné correctement: Classe 0;
- Précision 950-999: Classe 1;
- Précision 835-950: classe 2;
- Précision 720-834: classe 3;
- Précision 615-719: classe 4;
À l'intérieur de chaque classe, le score est le nombre d'octets que prend la solution.
Réponse acceptée: la plus petite solution dans la meilleure classe non vide.
Liens
- Projet Github avec outils: https://github.com/vi/codegolf-jein
- Ensemble de données de formation: http://vi-server.org/pub/codegolf-jein-train.tar.xz
- Le jeu de données d'examen est maintenu privé jusqu'à présent, il existe des sommes de contrôle (HMAC) disponibles dans le référentiel Github.
Tous les échantillons doivent être considérés comme CC-0 (domaine public), les scripts et les programmes doivent être considérés comme MIT.
Exemple de solution
Il offre une très mauvaise qualité de reconnaissance, montre simplement comment lire les fichiers et afficher les réponses
#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
ou devons-nous utiliserfoldl (+) 0
(foldl n'étant pas spécifique aux mathématiques et+
non variadique)?sum
. Je suppose que ce n'est pas ton intention?Réponses:
C ++ 11 (gcc;
163916251635 octets, classe 1, score = 983, 960)Commençons. C'est probablement le code le plus long que j'aie jamais raccourci ...
"Ungolfed" (bien qu'il soit difficile d'appeler un code source de plus de 1,5K golfé):
Je n'ai aucune idée de paniquer si cela fonctionnera correctement sur un ensemble de données réel (je parie que non, mais je dois essayer).
Comment ça marche:
log(mean distribution)+10
puis normalisé pour que la somme des plus grands pics soit de 1.dunno
.Comme je l'ai dit, probablement lors des tests finaux, il sera classé comme "encore pire que aléatoire". Bien sûr, j'espère que ce ne sera pas le cas: D
Edit: bug corrigé (oublié de fermer les fichiers).
la source
worse than random
. Vous n'avez littéralement besoin de changer qu'un octet -distYes > distNo
, et cela fera l'affairebetter than random
. Ou pour le dire autrement, il serait assez étonnant que vous deviniez le résultat d'un lancer de pièce de monnaie incorrectement 100 fois de suite! Et il n'est pas rare que des algorithmes simples soient plus performants que des algorithmes plus complexes, alors +1 et je vous souhaite bonne chance.EMFILE (Too many open files)
... Essayer de réparer ...Accuracy: 983 ‰; Time: 0m27.570s;
; ensemble de données d'examen:Accuracy: 960 ‰; Time: 0m32.957s
. Bon travail.#define
s: P