Ses tanıma: “Evet” veya “Hayır”?


33

Görev

Bir eğitim örneğini maksimum doğrulukla temel alarak, bir ses örneğinin ses tanıma özelliğini (sesli olarak "evet", "evet" veya "hayır" diyerek), ses hassasiyetinde sese veya fısıldamaya, sade veya tuhaf bir şekilde, minimum byte kaynak veya ikili kod programına uygulayın. .

Program okumalı train/yes0.wav, train/no0.wav, train/yes1.wavve böylece (400 yeses ve eğitim veri kümesi içinde 400 noes vardır), sonra okumaya başlamak inputs/0.wav,inputs/1.wav o "evet" veya "hayır" (ya da diğer kelime için analiz ve çıkışı, dosyayı bulamazsa kadar ara cevap).

İsterseniz, okuma yerine programı önceden eğitebilirsiniz. train/ , ancak sonuçta ortaya çıkan veri tablosu skora göre sayılır (ve eğitim örneklerine fazladan uymamaya dikkat edin - bunlar sınavlarla örtüşmez). Bu durumda veri tablosu oluşturmak için kullanılan programı bir zeyilname olarak eklemek daha iyidir.

Tüm örnek dosyalar filtreleme / gürültü azaltma olmadan, sadece dizüstü bilgisayar mikrofonundan küçük endian 16-bit stereo WAV dosyalarıdır.

Sınırları

Yasaklı özellikler:

  • Ağ kullanarak;
  • Cevap dosyasına ulaşmaya çalışıyorum inputs/key ;
  • Subverting runnerDoğruluğu hesaplayan programı ;
  • Mevcut tanıma kütüphanelerini kullanma. FFT uygulamasına bağlantı yapılmasına izin verilmez: yalnızca sabit miktarda bilgi alan harici matematik işlevlerine izin verilir ( sinveya gibi atan2); Eğer FFT istiyorsanız, sadece bunu programın kaynak koduna eklemeniz yeterli (gerekirse çok dilli olabilir).

Kaynak sınırları:

  • İ5 dizüstü bilgisayarımda programın 30 dakikadan fazla işlem yapmaması gerekiyor. Daha fazla alırsa, yalnızca ilk 30 dakikada üretilen çıktılar sayılır ve başka herhangi bir şeyin yarım eşleşme olduğu varsayılır;
  • Hafıza sınırı: 1GB (geçici dosyalar dahil);

Araçlar

tools/runnerProgram otomatik olarak çözümünü çalışır ve doğruluğu hesaplar.

$ tools/runner solutions/example train train/key 
Accuracy: 548 ‰

Eğitim verilerini kullanarak veya gerçek sınav verilerini kullanarak programı inceleyebilir. Veri setini halka açık hale getirene kadar sınav veri kümesinde gönderilen yanıtları deneyeceğim ve sonuçları (doğruluk yüzdesi) yayınlayacağım.

puanlama

Doğruluğa bağlı olarak 5 çözüm sınıfı vardır:

  • Tüm örnekler doğru tahmin edildi: Sınıf 0;
  • Doğruluk 950-999: Sınıf 1;
  • Doğruluk 835-950: Sınıf 2;
  • Doğruluk 720-834: Sınıf 3;
  • Doğruluk 615-719: Sınıf 4;

Her sınıfın içinde puan, çözümün aldığı bayt sayısıdır.

Kabul edilen cevap: en iyi kayıtsız sınıftaki en küçük çözüm.

Bağlantılar

Tüm örnekler CC-0 (Public Domain) olarak kabul edilmeli, scriptler ve programlar MIT olarak kabul edilmelidir.

Örnek çözüm

Çok düşük tanıma kalitesi sağlar, sadece dosyaların nasıl okunacağını ve cevapların çıktısını gösterir.

#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);

}

5
fft kütüphanesi yok mu? Niye ya?
John Dvorak

1
Yerleşik FFT işlevlerinden ne haber? Dışsal olarak tam olarak ne sayılır? Ayrıca, matematik kütüphanesi işlevi nedir? Kullanmamıza izin var mı sumveya kullanmamız gerekiyor mu foldl (+) 0(katlamaya matematiğe özgü +olmayan ve değişken olmayan)?
John Dvorak

1
hala ... etkili bir şekilde yasaklıyorsun sum. Sanırım niyetin bu değil mi?
John Dvorak

1
Matematik fonksiyonlarının tam tanımı nedir? Sayılarla çalışmak için uzmanlaşmış olanlar? Rakamlar için toplama, ancak dizeler için birleştirme kullanan genel bir "toplam" işlevi ne durumda? Bu meblağa şimdi izin veriliyor mu?
John Dvorak

1
Peki ya J'nin vektör işlemleri? İzin verilmedi mi?
John Dvorak

Yanıtlar:


27

C ++ 11 (gcc; 1639 1625 1635 bayt, Sınıf 1, puan = 983, 960)

Hadi başlayalım. Muhtemelen kısalttığım en uzun kod ...

#include <bits/stdc++.h>
#define $ complex<double>
#define C vector<$>
#define I int
#define D double
#define P pair<D,I>
#define Q pair<D,D>
#define E vector<D>
#define V vector<P>
#define W vector<Q>
#define S char*
#define Z(x)if(fread(&x,2,1,y)!=1)break;
#define B push_back
#define F(i,f,t)for(I i=f;i<t;i++)
#define _ return
#define J first
#define L second
const I K=75,O=16384;using namespace std;C R(C&i,I s,I o=0,I d=1){if(!s)_ C(1,i[o]);C l=R(i,s/2,o,d*2),h=R(i,s/2,o+d,d*2);C r(s*2);$ b=exp($(0,-M_PI/s)),f=1;F(k,0,s){r[k]=l[k]+f*h[k];r[k+s]=l[k]-f*h[k];f*=b;}_ r;}C T(C&i){_ R(i,i.size()/2);}char b[O];E U(S m){FILE*y;if(!(y=fopen(m,"r")))_ E();setvbuf(y,b,0,O);fseek(y,0,2);I z=ftell(y)/4-32;fseek(y,128,0);C p;F(i,0,z){short l,r;Z(l);Z(r);if(i&1)p.B($(0.5/O*le16toh(l),0));}p.resize(O);E h(O),n(O);p=T(p);F(j,0,O)h[j]=norm(p[j])/O;F(i,1,O-1)n[i]=(h[i-1]+h[i+1]+h[i]*8)/10;fclose(y);_ n;}W G(E&d){V p;F(i,3,O/2-3)if(d[i]==*max_element(d.begin()+i-3,d.begin()+i+4))p.B({d[i],i});sort(p.begin(),p.end(),greater<P>());W r;F(i,3,K+3)r.B({D(p[i].L)/O*22050,log(p[i].J)+10});D s=0;F(i,0,K)s+=r[i].L;F(i,0,K)r[i].L/=s;_ r;}char m[O];I X(S p,W&r){E t(O),h(t);I f=0;while(1){sprintf(m,"%s%d.wav",p,f++);h=U(m);if(!h.size())break;F(j,0,O)t[j]+=h[j];}F(j,0,O)t[j]/=f;r=G(t);}D Y(W&a,W&b){D r=0;F(i,0,K){D d=b[i].L;F(j,0,K)if(abs((b[i].J-a[j].J)/b[i].J)<0.015)d=min(d,abs(b[i].L-a[j].L));r+=d;}_ r;}I H(S p,W&y,W&n){I f=0;while(1){sprintf(m,"%s%d.wav",p,f++);E h=U(m);if(!h.size())break;W p=G(h);D q=Y(p,y),r=Y(p,n);printf(abs(q-r)<=0.01?"?\n":q<r?"yes\n":"no\n");}}I main(){W y,n;X("train/yes",y);X("train/no",n);H("inputs/",y,n);}

"Ungolfed" (1.5K'dan büyük bir kaynak kodunu golf oynamak zor olsa da):

#include <iostream>
#include <stdio.h>
#include <string>
#include <algorithm>
#include <vector>
#include <math.h>
#include <complex>
#include <endian.h>
#include <functional>

using namespace std;

typedef complex<double> CD;

vector<CD> run_fft(vector<CD>& input, int offset, int size, int dist){
    if(size == 1){
        return vector<CD>(1, input[offset]);
    }
    vector<CD> partLow = run_fft(input, offset, size/2, dist*2),
               partHi  = run_fft(input, offset+dist, size/2, dist*2);

    vector<CD> result(size);
    CD factorBase = exp(CD(0, (inv?2:-2)*M_PI/size)), factor = 1;

    for(int k = 0; k < size/2; k++){
        result[k] = partLow[k] + factor*partHi[k];
        result[k+size/2] = partLow[k] - factor*partHi[k];
        factor *= factorBase;
    }
    return result;
}

vector<CD> fft(vector<CD>& input){
    int N = input.size();
    return run_fft(input, 0, N, 1);
}



const int MAX_BUF = 65536;
const int PWR_TWO = 16384;
const int NUM_CHECK = 75;
int sampling;

char buf[MAX_BUF];
vector<double> read_data(char* filenam){
    FILE* fp = fopen(filenam, "r");
    if(!fp)
        return vector<double>();
    setvbuf(fp, buf, _IOFBF, MAX_BUF);

    fseek(fp, 0, SEEK_END);
    int filesiz = ftell(fp);
    fseek(fp, 128, SEEK_SET);
    filesiz -= 128;

    int insamp = filesiz / 4;
    int freqsamp = 2,
        act_mod = 0;
    sampling = 44100 / freqsamp;
    int inputSize;

    vector<CD> input;

    for(int i = 0; i < insamp; i++){
        signed short int l, r;
        if(fread(&l, 2, 1, fp) != 1) break;
        if(fread(&r, 2, 1, fp) != 1) break;

        double act = 1/32768.0 * (le16toh(l));

        if((++act_mod) == freqsamp){
            inputSize++;
            input.push_back(CD(act,0));
            act_mod = 0;
        }
    }
    inputSize = input.size();

    //printf("%s\n", filenam);
    int numParts = (inputSize+PWR_TWO-1)/PWR_TWO;
    double partDelta = (double)inputSize / numParts, actDelta = 0;
    vector<CD> ndata(PWR_TWO);
    for(int i = 0; i < numParts; i++){
        vector<CD> partInput(PWR_TWO);
        int from = floor(actDelta),
            to = floor(actDelta)+PWR_TWO;

        for(int j = from; j < to; j++)
            partInput[j-from] = input[j];

        vector<CD> partData = fft(partInput);
        for(int j = 0; j < PWR_TWO; j++)
            ndata[j] += partData[j]*(1.0/numParts);
    }


    vector<double> height(PWR_TWO);
    for(int i = 0; i < PWR_TWO; i++)
        height[i] = norm(ndata[i])/PWR_TWO;

    vector<double> nheight(height);
    nheight[0] = (height[0]*0.8 + height[1]*0.1)/0.9;
    nheight[PWR_TWO-1] = (height[PWR_TWO]*0.8 + height[PWR_TWO-1]*0.1)/0.9;
    for(int i = 1; i < PWR_TWO-1; i++)
        nheight[i] = height[i-1]*0.1 + height[i]*0.8 + height[i+1]*0.1;

    fclose(fp);

    return nheight;
}


vector< pair<double,double> > get_highest_peaks(vector<double>& freqData){
    vector< pair<double,int> > peaks;

    for(int i = 3; i < PWR_TWO/2-3; i++){
        if(freqData[i] == *max_element(freqData.begin()+i-3, freqData.begin()+i+4)){
            peaks.push_back(make_pair(freqData[i], i));
        }
    }

    sort(peaks.begin(), peaks.end(), greater< pair<double,int> >());

    vector< pair<double,double> > res;
    for(int i = 3; i < NUM_CHECK+3; i++){
        res.push_back(make_pair((double)(peaks[i].second)/PWR_TWO*sampling, log(peaks[i].first)+10));
    }

    double sum_res = 0;
    for(int i = 0; i < NUM_CHECK; i++)
        sum_res += res[i].second;
    for(int i = 0; i < NUM_CHECK; i++)
        res[i].second /= sum_res;

    /*for(int i = 0; i < NUM_CHECK; i++)
        printf("%12lf %12lf\n", res[i].first, res[i].second);
    printf("\n");*/

    return res;
}


void train(char* dir, const char* type, vector< pair<double,double> >& res){
    vector<double> result(PWR_TWO), height(PWR_TWO);

    int numFile = 0;
    while(true){
        char filenam[256];
        snprintf(filenam, 255, "%s/%s%d.wav", dir, type, numFile);
        height = read_data(filenam);

        if(height.size() == 0)
            break;

        for(int j = 0; j < PWR_TWO; j++)
            result[j] += height[j];

        numFile++;
    }
    fprintf(stderr, "trained %s on %d files\n", type, numFile);

    for(int j = 0; j < PWR_TWO; j++)
        result[j] /= numFile;

    res = get_highest_peaks(result);
}


double dist_ab(vector< pair<double,double> >& A, vector< pair<double,double> >& B){
    double result = 0;
    for(int i = 0; i < NUM_CHECK; i++){
        double add = B[i].second;

        for(int j = 0; j < NUM_CHECK; j++){
            double dist = (B[i].first-A[j].first)/B[i].first;
            if(abs(dist) < 0.015)
                add = min(add, abs(B[i].second - A[j].second));
        }
        result += add;
    }
    return result;
}


void trial(char* dir, const char* pref, vector< pair<double,double> >& yes,
                                        vector< pair<double,double> >& no){
    int numFile = 0;
    int numYes = 0, numDunno = 0, numNo = 0;
    while(true){
        char filenam[256];
        snprintf(filenam, 255, "%s/%s%d.wav", dir, pref, numFile);

        vector<double> height = read_data(filenam);
        if(height.size() == 0)
            break;

        vector< pair<double,double> > peaks = get_highest_peaks(height);


        double distYes = dist_ab(peaks, yes),
               distNo = dist_ab(peaks, no);

        if(abs(distYes-distNo) <= 0.01){
            printf("dunno\n");
            numDunno++;
        } else if(distYes < distNo){
            printf("yes\n");
            numYes++;
        } else {
            printf("no\n");
            numNo++;
        }
        //printf(" (%lf %lf)\n", distYes, distNo);

        numFile++;
    }
}


int main(int argc, char** argv){
    vector< pair<double,double> > yes, no;


    train("train", "yes", yes);
    train("train", "no", no);

    trial("inputs", "", yes, no);
}

Gerçek veri setinde doğru çalışacaksa kafayı yitirecek bir fikrim yok (Bahse girerim denemem gerekir).

Nasıl çalışır:

  1. Her biri eşit zaman aralığında N = 2 14 sol kanaldan numune alın. Normalleştirin ki minimum değer = 0 ve maksimum değer = 1 olsun.
  2. FFT kullanarak bunları işleyin. Şimdi zaman domeninden frekans domenine geçtik. Bu dizi elde edilen bu 0. hücre 0Hz eşdeğeri ve 2 diyebilir 13 -1st hücre (I L kanalından her örnek sürdüğü zaman örnekleme 22050Hz yerine WAV frekansını, 44100Hz yani olduğunu) 22050Hz eşdeğerdir.
  3. Tüm bu sinyallerin ortalamasını bulun - "ortalama frekans dağılımı" olarak adlandırın. Bu tür dağılımdaki en yüksek zirveyi bulun (burada K = 75), ilk birkaç kişiyi (muhtemelen gürültü) göz ardı edin ve kuvvetlerini bulun. log(mean distribution)+10En büyük zirvelerin toplamı 1 olacak şekilde kullandım ve normalize ettim .
  4. İki "pik dağılımımız" var - biri Evet, ikincisi Hayır. İkincisi, test etmek için bir WAV'ımız varsa, daha önce olduğu gibi değiştiririz (adım 1, 2, 3) ve D dağılımını elde ederiz. hangi dağılımın (E / H) D'nin daha benzer olduğunu kontrol edin. Aşağıdaki yaklaşımı kullandım: Y / N'deki her tepe noktası için, D'de bulmaya çalışın (yaklaşık olarak) bulursak, bu tepe noktasının puanı, Y / N'ler ve D'nin gücü arasındaki mutlak farktır; tam tersi durumda, bu Y / N'nin gücü (her zaman pozitif olduğunu varsayıyoruz). Daha iyi (daha küçük) puan kazanır. Sonuçlar çok yakınsa (0.01 mutlak fark kullandım), çıktı dunno.

Dediğim gibi, muhtemelen son testlerde "rastgele bile kötü" olarak sınıflandırılacaktır. Tabii ki, umarım olmaz: D

Düzenleme: sabit hata (dosyaları kapatmayı unuttum).


1
Gerçekleştiriyorsa şansın yaver gitmiştir worse than random. Kelimenin tam anlamıyla sadece bir bayt değiştirmek gerekir - distYes > distNove yapacak better than random. Ya da başka bir deyişle, bir madalyonun sonucunu arka arkaya 100 defa yanlış çevirdiğini tahmin edebiliyorsanız oldukça şaşırtıcı olurdu! Ve bu basit algoritmaların daha karmaşık olanlardan daha iyi performans göstermediğini duymadım, bu yüzden +1 ve size iyi şanslar diliyorum.
blutorange

Test Edildi ... Aşağıdaki nedenlerden dolayı erken sonlandırılıyor EMFILE (Too many open files)... Düzeltmeye çalışmak ...
Vi.

Çarptı maksimum açık dosya sayacı, şimdi çalışıyor. Sonuçlar: eğitim veri seti Accuracy: 983 ‰; Time: 0m27.570s;:; Sınav veri kümesi: Accuracy: 960 ‰; Time: 0m32.957s. Aferin.
Vi.

Tamam, bunu düzelttim. 10 bayt daha fazla. :)
mnbvmar

#define
S'nin
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.