Nhận dạng giọng nói: Có Có hay hay không?


33

Bài tập

Triển khai chương trình theo byte tối thiểu của mã nguồn hoặc mã nhị phân nhận dạng giọng nói của mẫu giọng nói (tôi nói "có", "vâng" hoặc "không" bằng giọng nói hoặc thì thầm, rõ ràng hoặc nhếch mép) dựa trên các mẫu đào tạo với độ chính xác tối đa .

Chương trình này nên đọc train/yes0.wav, train/no0.wav, train/yes1.wavvà vân vân (có 400 yeses và 400 Choáng trong tập dữ liệu huấn luyện), sau đó bắt đầu đọc inputs/0.wav, inputs/1.wavcho đến khi nó không tìm thấy tập tin, phân tích nó và xuất "có" hoặc "không" (hay nói cách khác cho câu trả lời trung gian).

Nếu bạn muốn, bạn có thể đào tạo trước chương trình thay vì đọc train/, nhưng bảng dữ liệu kết quả sẽ được tính vào điểm số (và hãy cẩn thận với việc quá mức cho các mẫu đào tạo - chúng không trùng với các bài kiểm tra). Tốt hơn là bao gồm chương trình được sử dụng để sản xuất bảng dữ liệu dưới dạng phụ lục trong trường hợp này.

Tất cả các tệp mẫu là các tệp WAV âm thanh nổi 16 bit nhỏ về cuối, chỉ từ mic của máy tính xách tay, không lọc / giảm nhiễu.

Giới hạn

Các tính năng bị cấm:

  • Sử dụng mạng;
  • Cố gắng để đạt được các tập tin câu trả lời inputs/key;
  • Phá vỡ runnerchương trình tính toán chính xác;
  • Sử dụng các thư viện nhận dạng hiện có. Không cho phép liên kết đến triển khai FFT: chỉ cho phép các hàm toán học bên ngoài lấy lượng thông tin không đổi (như sinhoặc atan2); Nếu bạn muốn FFT, chỉ cần thêm triển khai vào mã nguồn chương trình của bạn (nó có thể là đa ngôn ngữ nếu cần).

Giới hạn tài nguyên:

  • Chương trình không nên mất hơn 30 phút thời gian CPU trên máy tính xách tay i5 của tôi. Nếu phải mất nhiều hơn, chỉ có sản lượng được sản xuất trong 30 phút đầu tiên được tính và các công cụ truyền tải khác được giả định là một nửa khớp;
  • Giới hạn bộ nhớ: 1GB (bao gồm mọi tệp tạm thời);

Công cụ

Các tools/runnerchương trình sẽ tự động chạy giải pháp của bạn và tính toán chính xác.

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

Nó có thể kiểm tra chương trình bằng cách sử dụng dữ liệu đào tạo hoặc sử dụng dữ liệu thi thực tế. Tôi sẽ thử gửi câu trả lời về tập dữ liệu kiểm tra và công bố kết quả (tỷ lệ chính xác) cho đến khi tôi công khai bộ dữ liệu.

Chấm điểm

Có 5 lớp giải pháp tùy thuộc vào độ chính xác:

  • Tất cả các mẫu đoán đúng: Lớp 0;
  • Độ chính xác 950-999: Loại 1;
  • Độ chính xác 835-950: Loại 2;
  • Độ chính xác 720-834: Lớp 3;
  • Độ chính xác 615-719: Lớp 4;

Trong mỗi lớp, điểm số là số byte mà giải pháp lấy.

Câu trả lời được chấp nhận: giải pháp nhỏ nhất trong lớp không trống tốt nhất.

Liên kết

Tất cả các mẫu phải được coi là CC-0 (Miền công cộng), tập lệnh và chương trình nên được coi là MIT.

Giải pháp ví dụ

Nó cung cấp chất lượng nhận dạng rất kém, chỉ cho thấy cách đọc tệp và câu trả lời đầu ra

#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
không có thư viện fft? Tại sao?
John Dvorak

1
Điều gì về các chức năng FFT tích hợp? Chính xác những gì được tính là bên ngoài? Ngoài ra, những gì được tính là một chức năng thư viện toán học? Chúng tôi có được phép sử dụng không sum, hay chúng tôi cần sử dụng foldl (+) 0(Foldl không phải là đặc thù toán học, và +không bị biến dạng)?
John Dvorak

1
vẫn ... bạn đang cấm một cách hiệu quả sum. Tôi đoán đó không phải là ý định của bạn?
John Dvorak

1
Định nghĩa chính xác của các hàm toán học là gì? Những người chuyên hoạt động trên số? Điều gì về một hàm "tổng" chung sử dụng phép cộng cho số, nhưng nối chuỗi cho chuỗi? Là số tiền này bây giờ được phép?
John Dvorak

1
Những gì về hoạt động vector của J? Họ không được phép?
John Dvorak

Câu trả lời:


27

C ++ 11 (gcc; 1639 1625 1635 byte, Lớp 1, điểm = 983, 960)

Bắt đầu thôi. Đây có lẽ là mã dài nhất tôi từng rút ngắn ...

#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" (mặc dù thật khó để gọi mã nguồn hơn 1,5K được đánh gôn):

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

Tôi không có ý tưởng kỳ quặc nếu nó sẽ hoạt động chính xác trên tập dữ liệu thực (tôi cá là nó sẽ không, nhưng tôi phải thử).

Làm thế nào nó hoạt động:

  1. Lấy N = 2 14 mẫu từ kênh trái, mỗi mẫu trong khoảng thời gian bằng nhau. Bình thường hóa chúng sao cho giá trị tối thiểu = 0 và giá trị tối đa = 1.
  2. Xử lý chúng bằng FFT. Bây giờ chúng tôi chuyển từ miền thời gian sang miền tần số. Chúng ta có thể nói rằng ô thứ 0 của mảng kết quả là tương đương 0Hz và 2 ô 13 -1 tương đương 22050Hz (đó là do tôi lấy mọi mẫu khác từ kênh L, vì vậy mẫu của tôi là 22050Hz thay vì tần số WAV, 44100Hz).
  3. Tìm giá trị trung bình của tất cả các tín hiệu như vậy - gọi nó là "phân phối tần số trung bình". Tìm K đỉnh cao nhất trong phân phối như vậy (ở đây K = 75), bỏ qua vài điểm đầu tiên (có thể là nhiễu) và tìm cường độ của chúng. Tôi đã sử dụng log(mean distribution)+10và sau đó chuẩn hóa để tổng các đỉnh lớn nhất là 1.
  4. Chúng tôi có hai "phân phối cao nhất" - một cho Có, thứ hai là Không. Nếu chúng tôi có WAV để kiểm tra, chúng tôi sẽ chuyển đổi nó theo cách tương tự như trước (bước 1, 2, 3) và nhận phân phối D. Sau đó, chúng tôi phải kiểm tra phân phối nào (Y / N) D giống với. Tôi đã sử dụng cách tiếp cận sau: đối với mỗi đỉnh trong Y / N, hãy thử tìm nó ở D. Nếu chúng ta tìm thấy nó (xấp xỉ), điểm cho đỉnh này là sự khác biệt tuyệt đối giữa sức mạnh của Y / N và D; trong trường hợp ngược lại, đó là sức mạnh của Y / N (chúng tôi cho rằng nó luôn luôn tích cực). Điểm số tốt hơn (nhỏ hơn) chiến thắng. Nếu kết quả rất gần (tôi sử dụng 0,01 chênh lệch tuyệt đối), đầu ra dunno.

Như tôi đã nói, có lẽ trong các thử nghiệm cuối cùng, nó sẽ được phân loại là "thậm chí còn tệ hơn ngẫu nhiên". Tất nhiên, tôi hy vọng nó sẽ không: D

Chỉnh sửa: đã sửa lỗi (quên đóng các tệp).


1
Bạn là người may mắn nếu nó thực hiện worse than random. Bạn thực sự chỉ cần thay đổi một byte - distYes > distNovà nó sẽ làm được better than random. Hay nói cách khác, sẽ khá ngạc nhiên nếu bạn có thể đoán được kết quả của việc lật đồng xu không chính xác 100 lần liên tiếp! Và không có gì lạ khi các thuật toán đơn giản đó hoạt động tốt hơn các thuật toán phức tạp hơn, vì vậy +1 và tôi chúc bạn may mắn.
blutorange

Kiểm tra ... Nó chấm dứt sớm do EMFILE (Too many open files)... Đang cố sửa ...
Vi.

Bumped truy cập tập tin mở tối đa, bây giờ nó hoạt động. Kết quả: tập dữ liệu đào tạo : Accuracy: 983 ‰; Time: 0m27.570s;; kiểm tra dữ liệu : Accuracy: 960 ‰; Time: 0m32.957s. Làm tốt lắm.
Vi.

Được rồi, tôi đã sửa cái này. Thêm 10 byte. :)
mnbvmar

Sử dụng đáng kinh ngạc của #defines: P
qwr
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.