딥러닝과 노이즈캔슬링이 만났을 때 Show 통화를 할 때 주위 잡음을 없애주는 노이즈 캔슬링은 편안한 대화를 위해 필수적이라고 할 수 있다. 사실 일상 자체는 수많은 주위 소음에 둘러싸여 있다. 시내나 공항에서 전화를 걸면 이 같은 주위 소음까지 상대방에게 들린다. 문제는 항상 완벽하게 소음을 차단해주는 제품을 찾기는 쉽지 않다는 것이다. 그런데 2Hz라는 프로젝트가 딥러닝을 활용해 실용적인 노이즈 캔슬링 기능을 구축하려는 도전을 진행하고 있다. 러닝을 통해 구축한 노이즈 캔슬링 시스템이 그것. 데모 영상을 보면 환경 소음이 귀에 거슬리는 정도 환경에서 말을 녹음한다. 녹음된 음성은 당연히 잡음이 많아 알아 듣기 힘들다. 여기에 2Hz 노이즈 캔슬링을 하면 소리가 선명하게 들린다. 배경 속 소음은 거의 없다고 해도 과연이 아니다. 또 경찰 순찰차가 울리는 사이렌 소리와 함께 녹음한 음성 역시 2Hz 시스템을 통해 재생하면 선명하게 사람 목소리만 들리고 사이렌이 뒤에서 올리는지 여부조차 알 수 없게 된다. 엔비디아는 이렇게 높은 수준으로 노이즈 캔슬링 기능을 실현했지만 이 같은 잡음을 억제하는 시스템 구축은 상당히 어려웠다고 한다.
2Hz가 구현한 노이즈 캔슬링 기능은 먼저 전화를 거는 쌍방 뒤에서 들리는 소음을 상대방에게 보낼 때 억제한다. 이와 대조적인 게 이어폰 등에 들어간 액티브 노이즈캔슬링 ANC(Active Noise Cancellation)다. ANC는 자신의 뒤에서 울리는 소음을 이어폰이나 헤드폰이 감지해 자신의 귀에 닿지 않게 차단해주는 것이다. 이에 비해 2Hz 측은 어디까지나 상대방에게 들리는 잡음을 억제하는 기능에 초점을 맞추고 있다. 통화할 때 잡음을 억제하는 기능은 최근 상당한 기술 발전을 했다. 최신 스마트폰에선 10년 전부터 현격하게 소음이 줄었고 이런 기능은 마이크 2개나 그 이상으로 구현한 것이다. 하나는 사용자가 통화할 때 입에서 나온 목소리가 잘 닿는 부위, 다른 하나는 입가 마이크에서 가능하면 멀리 뒤쪽 등 환경음이 잘 닿는 부분에 설치되는 게 보통이다. 입가 마이크가 소리를 주로 끌어온다면 후면 마이크는 주위 소리를 모아 장치 내부 소프트웨어를 통해 걸러내 깨끗하게 만들어주는 구조로 노이즈 캔슬링을 한다. 하지만 이 기술은 스마트워치처럼 작은 마이크 2개 이상을 떨어진 곳에 설치할 수 없는 기기 혹은 사용자나 장치 위치가 떨어져 있거나 흔들리고 있다면 제대로 작동하지 않는 게 흠이다. 또 마이크를 복수 탑재한다는 면에서 생산 비용도 올라간다. 2Hz는 이런 이유로 마이크 여러 개가 아닌 단일 마이크를 이용한 노이즈 캔슬링을 구현할 구조를 만들어냈다. 음성 배경에 들리는 잡음만 제거하는 노이즈 캔슬링에선 디지털 신호 처리 알고리즘이 많이 쓰인다. 이런 DSP 알고리즘은 연속적인 정상 소음을 차단할 때에는 잘 작동한다. 하지만 아기 울음소리와 사이렌 소리 등 짧고 빠른 소음에는 제대로 대처하지 못한다. 2Hz는 기존 노이즈 캔슬링으로 버거웠던 소음에 대해서도 대체할 수 있도록 딥러닝을 이용했다. 딥러닝을 이용한 노이즈 캔슬링 시스템 구축 방법은 이렇다. 잡음 소리와 깨끗한 음성이라는 2가지 종류 음성 데이터를 제공하고 이를 합친 잡음 들어간 음성을 만들어낸다. 그리고 깨끗한 음성 데이터와 인공 잡음 데이터를 심층신경망 DNN에 입력한다. 그리고나서 입력한 잡음을 음성 데이터에서 제거해 깨끗한 음성 데이터를 출력하도록 훈련시킨다. 깨끗한 음성 데이터를 추출할 수 있는 마스크를 만들어 딥러닝을 활용한 노이즈 캔슬링 시스템을 만드는 것이다. 2Hz 프로젝트를 통해 자체 DNN 아키텍처를 개발하고 다양한 잡음에 대처할 수 있는 마스크를 만들어낼 수 있게 됐다고 한다. 음성 통화에서 노이즈 캔슬링을 이용할 때 문제가 되는 것 중 하나는 음성 지연이다. 사람은 실시간 대화를 할 때 0.2초까지 지연 시간은 견딜 수 있지만 이 이상이 되면 부드러운 대화를 할 수 없게 된다. 회선과 컴퓨터, 코딩 등 3가지 요소가 통화 지연에 영향을 미치지만 보통 가장 크게 지연 시간에 영향을 미치는 건 회선 상황이다. 하지만 DNN을 이용해 노이즈 캔슬링을 하면 실제 통화에 지장이 생길 만큼 지연 시간이 생길 가능성을 부정할 수 없다. 이런 이유로 고품질 노이즈 캔슬링 통화를 지원하려면 이를 처리할 컴퓨터 성능을 높일 필요가 있다. 하지만 통화에 이용하는 스마트폰 같은 장치에 노이즈 캔슬링을 위한 고사양 컴퓨팅을 탑재하는 건 현실적이지 않다. 엔비디아는 이런 이유로 노이즈 캔슬링 도구를 클라우드화한다는 발상을 했다. 노이즈 캔슬링 시스템은 소프트웨어 기반이기 때문에 반드시 로컬 장치 자체에 탑재하고 있을 필요는 없다는 것이다. 거대 VoIP 공급자라면 대량 통화를 동시 처리해야 한다. 보통 미디어 서버 하나로 G.711이라는 음성 부호를 이용한 통화를 동시에 3,000건 처리하고 있다고 한다. 노이즈 캔슬링을 VoIP 음성 통화 시스템에 통합하면 서버 측 처리가 늦어져 자칫 서비스 품질에 악영향을 줄 수 있다. 2Hz 프로젝트는 CPU를 이용해 처리를 시도했다가 비용 효율적인 결과를 얻을 수 없었다고 밝히고 있다. 이런 이유로 엔비디아의 GPU인 GTX1080 Ti를 이용해 노이즈캔슬링을 포함한 VoIP 처리를 실험한 결과 서버 최적화 없이 1,000건 통화를 동시 처리할 수 있었고 최적화를 통해 3,000건 동시 통화가 가능하게 됐다고 한다. 음성 전송이나 코딩 같은 기본 처리는 CPU가 하고 GPU는 노이즈 캔슬링 일괄 처리를 맡아 기존 VoIP 처리에 영향을 안 주고 잡음을 최대한 억제했다는 것이다. GPU는 3D 그래픽 처리에 맞는 대규모 병렬 처리를 전문으로 한다. 또 엔비디아가 지난 몇 년 사이 급성장한 배경에서 알 수 있듯 딥러닝 등 일괄 처리에 GPU가 주목받고 있다. 엔비디아 쪽은 이런 점에서 딥러닝을 이용한 노이즈 캔슬링 일괄 처리에도 GPU가 적합하다고 밝히고 있다. 이런 점을 보면 이번 2Hz 역시 GPU의 활용 시장 확대 측면의 연구 개발이라고 볼 수 있겠지만 이 같은 노이즈 캔슬링에 딥러닝 등 AI 관련 기술을 도입하는 건 꽤 눈길을 시도인 것도 분명하다. 관련 내용은 이곳에서 확인할 수 있다. 이석원 기자월간 아하PC, HowPC 잡지시대를 거쳐 지디넷, 전자신문인터넷 부장, 컨슈머저널 이버즈 편집장, 테크홀릭 발행인, 벤처스퀘어 편집장 등 온라인 IT 매체에서 '기술시대'를 지켜봐 왔다. 여전히 활력 넘치게 변화하는 이 시장이 궁금하다. 딥러닝 신경망을 사용하여 음성 잡음 제거하기이 예제에서는 딥러닝 신경망을 사용하여 음성 신호의 잡음을 제거하는 방법을 다룹니다. 이 예제에서는 동일한 작업에 적용된 두 가지 유형의 신경망, 즉 완전 연결 신경망과 컨벌루션 신경망을 비교합니다. 소개음성 잡음 제거의 목표는 음성 신호에서 잡음을 제거하는 한편 음성의 품질과 명료함을 향상하는 것입니다. 이 예제에서는 딥러닝 신경망을 사용하여 음성 신호에서 세탁기 잡음을 제거하는 방법을 보여줍니다. 이 예제에서는 동일한 작업에 적용된 두 가지 유형의 신경망, 즉 완전 연결 신경망과 컨벌루션 신경망을 비교합니다. 문제 개요8kHz로 샘플링된 다음과 같은 음성 신호가 있다고 가정하겠습니다. [cleanAudio,fs] = audioread("SpeechDFT-16-8-mono-5secs.wav");
sound(cleanAudio,fs) 음성 신호에 세탁기 잡음을 추가합니다. 신호 대 잡음비(SNR)가 0dB이 되도록 잡음 전력을 설정합니다. noise = audioread("WashingMachine-16-8-mono-1000secs.mp3"); % Extract a noise segment from a random location in the noise file ind = randi(numel(noise) - numel(cleanAudio) + 1,1,1); noiseSegment = noise(ind:ind + numel(cleanAudio) - 1); speechPower = sum(cleanAudio.^2); noisePower = sum(noiseSegment.^2); noisyAudio = cleanAudio + sqrt(speechPower/noisePower)*noiseSegment; 잡음이 있는 음성 신호를 들어봅니다. 원래 신호와 잡음이 있는 신호를 시각화합니다. t = (1/fs)*(0:numel(cleanAudio) - 1); figure(1) tiledlayout(2,1) nexttile plot(t,cleanAudio) title("Clean Audio") grid on nexttile plot(t,noisyAudio) title("Noisy Audio") xlabel("Time (s)") grid on 음성 잡음 제거의 목표는 음성 신호에서 세탁기 소음을 제거하는 한편 출력 신호에서 원치 않는 아티팩트를 최소화하는 것입니다. 데이터셋 검토하기이 예제에서는 Mozilla Common Voice 데이터셋 [1]의 서브셋을 사용하여 딥러닝 신경망을 훈련시키고 테스트합니다. 데이터 세트에는 연구대상자가 말하는 짧은 문장을 48kHz로 녹음한 내용이 담겨 있습니다. 데이터 세트를 다운로드하고 다운로드한 파일의 압축을 풉니다. downloadFolder = matlab.internal.examples.downloadSupportFile("audio","commonvoice.zip"); dataFolder = tempdir; unzip(downloadFolder,dataFolder) dataset = fullfile(dataFolder,"commonvoice");
adsTrain = audioDatastore(fullfile(dataset,"train"),IncludeSubfolders=true); speedupExample = true; if speedupExample adsTrain = shuffle(adsTrain); adsTrain = subset(adsTrain,1:1000); end
[audio,adsTrainInfo] = read(adsTrain); 음성 신호를 들어봅니다. sound(audio,adsTrainInfo.SampleRate) 음성 신호를 플로팅합니다. figure(2) t = (1/adsTrainInfo.SampleRate) * (0:numel(audio)-1); plot(t,audio) title("Example Speech Signal") xlabel("Time (s)") grid on 딥러닝 시스템 개요아래에는 기본적인 딥러닝 훈련 도식이 나와 있습니다. 음성은 일반적으로 4kHz보다 낮으므로 먼저 깨끗한 오디오 신호와 잡음이 있는 오디오 신호를 8kHz로 다운샘플링하여 신경망의 계산 부하를 줄일 것입니다. 예측 변수 및 목표 신경망 신호는 각각 잡음이 있는 오디오 신호와 깨끗한 오디오 신호의 크기 스펙트럼입니다. 신경망 출력값은 잡음이 제거된 신호의 크기 스펙트럼입니다. 회귀 신경망은 예측 변수 입력값을 사용하여 출력 목표와 입력 목표 사이의 평균 제곱 오차를 최소화합니다. 잡음이 제거된 오디오는 출력 크기 스펙트럼과 잡음이 있는 신호의 위상을 사용하여 시간 영역으로 다시 변환됩니다 [2]. 윈도우 길이 256개 샘플, 중첩 75% 및 해밍 윈도우를 갖는 단시간 푸리에 변환(STFT)을 사용하여 오디오를 주파수 영역으로 변환합니다. 음수 주파수에 해당하는 주파수 샘플을 제외하여 스펙트럼 벡터의 크기를 129로 줄입니다(시간 영역 음성 신호는 실수이므로 이는 정보 손실로 이어지지 않음). 예측 변수 입력값은 8개의 잡음이 있는 연속된 STFT 벡터로 구성되어 있어 각 STFT 출력 추정값은 잡음이 있는 현재 STFT 1개와 잡음이 있는 이전 STFT 벡터 7개를 바탕으로 계산됩니다. STFT 목표값과 예측 변수이 섹션에서는 하나의 훈련 파일에서 목표 신호와 예측 변수 신호를 생성하는 방법을 보여줍니다. 먼저 시스템 파라미터를 정의합니다. windowLength = 256;
win = hamming(windowLength,"periodic");
overlap = round(0.75*windowLength);
fftLength = windowLength;
inputFs = 48e3;
fs = 8e3;
numFeatures = fftLength/2 + 1;
numSegments = 8;
src = dsp.SampleRateConverter(InputSampleRate=inputFs,OutputSampleRate=fs,Bandwidth=7920);
오디오 길이가 샘플 레이트 변환기 데시메이션 인자의 배수여야 합니다. decimationFactor = inputFs/fs; L = floor(numel(audio)/decimationFactor); audio = audio(1:decimationFactor*L); 오디오 신호를 8kHz로 변환합니다. audio = src(audio); reset(src) 세탁기 잡음 벡터에서 무작위 잡음 세그먼트를 만듭니다. randind = randi(numel(noise) - numel(audio),[1 1]); noiseSegment = noise(randind:randind + numel(audio) - 1); SNR이 0dB이 되도록 음성 신호에 잡음을 추가합니다. noisePower = sum(noiseSegment.^2); cleanPower = sum(audio.^2); noiseSegment = noiseSegment.*sqrt(cleanPower/noisePower); noisyAudio = audio + noiseSegment;
cleanSTFT = stft(audio,Window=win,OverlapLength=overlap,fftLength=fftLength); cleanSTFT = abs(cleanSTFT(numFeatures-1:end,:)); noisySTFT = stft(noisyAudio,Window=win,OverlapLength=overlap,fftLength=fftLength); noisySTFT = abs(noisySTFT(numFeatures-1:end,:)); 잡음이 있는 STFT에서 8개 세그먼트 크기의 훈련 예측 변수 신호를 생성합니다. 연속된 예측 변수 간의 중첩은 7개 세그먼트입니다. noisySTFT = [noisySTFT(:,1:numSegments - 1),noisySTFT]; stftSegments = zeros(numFeatures,numSegments,size(noisySTFT,2) - numSegments + 1); for index = 1:size(noisySTFT,2) - numSegments + 1 stftSegments(:,:,index) = noisySTFT(:,index:index + numSegments - 1); end 목표값과 예측 변수를 설정합니다. 두 변수의 마지막 차원은 오디오 파일에 의해 생성된 서로 다른 예측 변수/목표값 쌍의 개수에 해당합니다. 각 예측 변수는 129×8이고 각 목표값은 129×1입니다. targets = cleanSTFT; size(targets) predictors = stftSegments; size(predictors) tall형 배열을 사용하여 특징 추출하기처리 속도를 높이려면 tall형 배열을 사용하여 데이터저장소에 있는 모든 오디오 파일의 음성 세그먼트에서 특징 시퀀스를 추출하십시오. 메모리 내 배열과 달리 tall형 배열은 일반적으로 먼저 데이터저장소를 tall형 배열로 변환합니다. reset(adsTrain) T = tall(adsTrain) Starting parallel pool (parpool) using the 'local' profile ... Connected to the parallel pool (number of workers: 6). T = M×1 tall cell array {234480×1 double} {210288×1 double} {282864×1 double} {292080×1 double} {410736×1 double} {303600×1 double} {326640×1 double} {233328×1 double} : : : : 위 화면을 보면 (데이터저장소에 있는 파일의 개수에 해당하는) 행 개수 값 M을 아직 알 수 없음을 알 수 있습니다. M은 계산이 완료되기 전까지 자리 표시자로 기능합니다. tall형 테이블에서 목표값 및 예측 변수 크기 STFT를 추출합니다. 이 작업은 후속 계산에서 사용할 새로운 tall형 배열 변수를 만듭니다. 함수 [targets,predictors] = cellfun(@(x)HelperGenerateSpeechDenoisingFeatures(x,noise,src),T,UniformOutput=false);
[targets,predictors] = gather(targets,predictors); Evaluating tall expression using the Parallel Pool 'local': - Pass 1 of 1: Completed in 52 sec Evaluation completed in 1 min 53 sec 모든 특징을 평균 0, 표준편차 1로 정규화하는 것이 좋습니다. 예측 변수와 목표값의 평균과 표준편차를 각각 계산하고 이를 사용하여 데이터를 정규화합니다. predictors = cat(3,predictors{:}); noisyMean = mean(predictors(:)); noisyStd = std(predictors(:)); predictors(:) = (predictors(:) - noisyMean)/noisyStd; targets = cat(2,targets{:}); cleanMean = mean(targets(:)); cleanStd = std(targets(:)); targets(:) = (targets(:) - cleanMean)/cleanStd; 딥러닝 신경망에 필요한 차원으로 예측 변수와 목표값의 형태를 변경합니다. predictors = reshape(predictors,size(predictors,1),size(predictors,2),1,size(predictors,3)); targets = reshape(targets,1,1,size(targets,1),size(targets,2)); 훈련 중에 데이터의 1%를 검증용으로 사용할 것입니다. 검증은 신경망이 훈련 데이터에 과적합되는 경우를 발견하는 데 유용합니다. 데이터를 훈련 세트와 검증 세트로 무작위로 분할합니다. inds = randperm(size(predictors,4)); L = round(0.99*size(predictors,4)); trainPredictors = predictors(:,:,:,inds(1:L)); trainTargets = targets(:,:,:,inds(1:L)); validatePredictors = predictors(:,:,:,inds(L+1:end)); validateTargets = targets(:,:,:,inds(L+1:end)); 완전 연결 계층을 사용한 음성 잡음 제거먼저 완전 연결 계층으로 구성된 잡음 제거 신경망을 살펴보겠습니다. 완전 연결 계층의 각 뉴런은 이전 계층의 모든 활성화에 연결됩니다. 완전 연결 계층은 입력값에 가중치 행렬을 곱한 다음 편향 벡터를 더합니다. 가중치 행렬과 편향 벡터의 차원은 계층의 뉴런 개수와 이전 계층의 활성화 개수로 결정됩니다. 신경망의 계층을 정의합니다. 입력 크기를 layers = [ imageInputLayer([numFeatures,numSegments]) fullyConnectedLayer(1024) batchNormalizationLayer reluLayer fullyConnectedLayer(1024) batchNormalizationLayer reluLayer fullyConnectedLayer(numFeatures) regressionLayer ]; 다음으로, 신경망의 훈련 옵션을 지정합니다. 신경망이 훈련 데이터를 3번 통과하도록
miniBatchSize = 128; options = trainingOptions("adam", ... MaxEpochs=3, ... InitialLearnRate=1e-5,... MiniBatchSize=miniBatchSize, ... Shuffle="every-epoch", ... Plots="training-progress", ... Verbose=false, ... ValidationFrequency=floor(size(trainPredictors,4)/miniBatchSize), ... LearnRateSchedule="piecewise", ... LearnRateDropFactor=0.9, ... LearnRateDropPeriod=1, ... ValidationData={validatePredictors,validateTargets});
downloadPretrainedSystem = false; if downloadPretrainedSystem downloadFolder = matlab.internal.examples.downloadSupportFile("audio","SpeechDenoising.zip"); dataFolder = tempdir; unzip(downloadFolder,dataFolder) netFolder = fullfile(dataFolder,"SpeechDenoising"); s = load(fullfile(netFolder,"denoisenet.mat")); denoiseNetFullyConnected = s.denoiseNetFullyConnected; cleanMean = s.cleanMean; cleanStd = s.cleanStd; noisyMean = s.noisyMean; noisyStd = s.noisyStd; else denoiseNetFullyConnected = trainNetwork(trainPredictors,trainTargets,layers,options); end 신경망의 완전 연결 계층의 가중치 개수를 계산합니다. numWeights = 0; for index = 1:numel(denoiseNetFullyConnected.Layers) if isa(denoiseNetFullyConnected.Layers(index),"nnet.cnn.layer.FullyConnectedLayer") numWeights = numWeights + numel(denoiseNetFullyConnected.Layers(index).Weights); end end disp("Number of weights = " + numWeights); Number of weights = 2237440 컨벌루션 계층을 사용한 음성 잡음 제거완전 연결 계층 대신 컨벌루션 계층을 사용하는 신경망을 살펴보겠습니다 [3]. 2차원 컨벌루션 계층은 입력값에 슬라이딩 필터를 적용합니다. 이 계층은 입력값의 세로와 가로 방향을 따라 필터를 이동하면서 가중치와 입력값의 내적을 계산한 다음 편향 항을 추가하여 입력값을 컨벌루션합니다. 일반적으로 컨벌루션 계층은 완전 연결 계층보다 파라미터 개수가 적습니다. [3]에서 설명된 16개의 컨벌루션 계층으로 구성된 완전 컨벌루션 신경망의 계층을 정의합니다. 처음 15개의 컨벌루션 계층은 필터 너비가 각각 9, 5, 9이고 필터 개수가 각각 18, 30, 8인 3개 계층으로 구성된 그룹이 5번 반복된 것입니다. 마지막 컨벌루션 계층에는 필터 너비가 129인 필터가 1개 있습니다. 이 신경망에서 컨벌루션은 (주파수 차원을 따라) 한 방향으로만 수행되고, 시간 차원의 필터 너비는 첫 번째 계층을 제외한 모든 계층에 대해 1로 설정됩니다. 완전 연결 신경망과 마찬가지로, 컨벌루션 계층 뒤에는 ReLu 계층과 배치 정규화 계층이 옵니다. layers = [imageInputLayer([numFeatures,numSegments]) convolution2dLayer([9 8],18,Stride=[1 100],Padding="same") batchNormalizationLayer reluLayer repmat( ... [convolution2dLayer([5 1],30,Stride=[1 100],Padding="same") batchNormalizationLayer reluLayer convolution2dLayer([9 1],8,Stride=[1 100],Padding="same") batchNormalizationLayer reluLayer convolution2dLayer([9 1],18,Stride=[1 100],Padding="same") batchNormalizationLayer reluLayer],4,1) convolution2dLayer([5 1],30,Stride=[1 100],Padding="same") batchNormalizationLayer reluLayer convolution2dLayer([9 1],8,Stride=[1 100],Padding="same") batchNormalizationLayer reluLayer convolution2dLayer([129 1],1,Stride=[1 100],Padding="same") regressionLayer ]; 훈련 옵션은 검증 목표 신호의 차원이 회귀 계층에 필요한 차원과 같아지도록 치환된다는 점을 제외하면 완전 연결 신경망의 옵션과 동일합니다. options = trainingOptions("adam", ... MaxEpochs=3, ... InitialLearnRate=1e-5, ... MiniBatchSize=miniBatchSize, ... Shuffle="every-epoch", ... Plots="training-progress", ... Verbose=false, ... ValidationFrequency=floor(size(trainPredictors,4)/miniBatchSize), ... LearnRateSchedule="piecewise", ... LearnRateDropFactor=0.9, ... LearnRateDropPeriod=1, ... ValidationData={validatePredictors,permute(validateTargets,[3 1 2 4])});
downloadPretrainedSystem = false; if downloadPretrainedSystem downloadFolder = matlab.internal.examples.downloadSupportFile("audio","SpeechDenoising.zip"); dataFolder = tempdir; unzip(downloadFolder,dataFolder) netFolder = fullfile(dataFolder,"SpeechDenoising"); s = load(fullfile(netFolder,"denoisenet.mat")); denoiseNetFullyConvolutional = s.denoiseNetFullyConvolutional; cleanMean = s.cleanMean; cleanStd = s.cleanStd; noisyMean = s.noisyMean; noisyStd = s.noisyStd; else denoiseNetFullyConvolutional = trainNetwork(trainPredictors,permute(trainTargets,[3 1 2 4]),layers,options); end 신경망의 완전 연결 계층의 가중치 개수를 계산합니다. numWeights = 0; for index = 1:numel(denoiseNetFullyConvolutional.Layers) if isa(denoiseNetFullyConvolutional.Layers(index),"nnet.cnn.layer.Convolution2DLayer") numWeights = numWeights + numel(denoiseNetFullyConvolutional.Layers(index).Weights); end end disp("Number of weights in convolutional layers = " + numWeights); Number of weights in convolutional layers = 31812 잡음 제거 신경망 테스트하기테스트 데이터 세트를 읽어 들입니다. adsTest = audioDatastore(fullfile(dataset,"test"),IncludeSubfolders=true);
데이터저장소에서 파일의 내용을 읽어 들입니다. [cleanAudio,adsTestInfo] = read(adsTest); 오디오 길이가 샘플 레이트 변환기 데시메이션 인자의 배수여야 합니다. L = floor(numel(cleanAudio)/decimationFactor); cleanAudio = cleanAudio(1:decimationFactor*L); 오디오 신호를 8kHz로 변환합니다. cleanAudio = src(cleanAudio); reset(src) 이 테스트 단계에서는 훈련 단계에서 사용되지 않는 세탁기 잡음으로 음성을 손상시킵니다. noise = audioread("WashingMachine-16-8-mono-200secs.mp3"); 세탁기 잡음 벡터에서 무작위 잡음 세그먼트를 만듭니다. randind = randi(numel(noise) - numel(cleanAudio), [1 1]); noiseSegment = noise(randind:randind + numel(cleanAudio) - 1); SNR이 0dB이 되도록 음성 신호에 잡음을 추가합니다. noisePower = sum(noiseSegment.^2); cleanPower = sum(cleanAudio.^2); noiseSegment = noiseSegment.*sqrt(cleanPower/noisePower); noisyAudio = cleanAudio + noiseSegment;
noisySTFT = stft(noisyAudio,Window=win,OverlapLength=overlap,fftLength=fftLength); noisyPhase = angle(noisySTFT(numFeatures-1:end,:)); noisySTFT = abs(noisySTFT(numFeatures-1:end,:)); 잡음이 있는 STFT에서 8개 세그먼트 크기의 훈련 예측 변수 신호를 생성합니다. 연속된 예측 변수 간의 중첩은 7개 세그먼트입니다. noisySTFT = [noisySTFT(:,1:numSegments-1) noisySTFT]; predictors = zeros(numFeatures,numSegments,size(noisySTFT,2) - numSegments + 1); for index = 1:(size(noisySTFT,2) - numSegments + 1) predictors(:,:,index) = noisySTFT(:,index:index + numSegments - 1); end 훈련 단계에서 계산한 평균과 표준편차를 사용하여 예측 변수를 정규화합니다. predictors(:) = (predictors(:) - noisyMean)/noisyStd; 2개의 훈련된 신경망으로 predictors = reshape(predictors,[numFeatures,numSegments,1,size(predictors,3)]); STFTFullyConnected = predict(denoiseNetFullyConnected,predictors); STFTFullyConvolutional = predict(denoiseNetFullyConvolutional,predictors); 훈련 단계에서 사용한 평균과 표준편차로 출력값을 스케일링합니다. STFTFullyConnected(:) = cleanStd*STFTFullyConnected(:) + cleanMean; STFTFullyConvolutional(:) = cleanStd*STFTFullyConvolutional(:) + cleanMean; 단측 STFT를 중심이 맞춰진 STFT로 변환합니다. STFTFullyConnected = (STFTFullyConnected.').*exp(1j*noisyPhase); STFTFullyConnected = [conj(STFTFullyConnected(end-1:-1:2,:));STFTFullyConnected]; STFTFullyConvolutional = squeeze(STFTFullyConvolutional).*exp(1j*noisyPhase); STFTFullyConvolutional = [conj(STFTFullyConvolutional(end-1:-1:2,:));STFTFullyConvolutional]; 잡음이 제거된 음성 신호를 계산합니다. denoisedAudioFullyConnected = istft(STFTFullyConnected,Window=win,OverlapLength=overlap,fftLength=fftLength,ConjugateSymmetric=true); denoisedAudioFullyConvolutional = istft(STFTFullyConvolutional,Window=win,OverlapLength=overlap,fftLength=fftLength,ConjugateSymmetric=true); 깨끗한 오디오 신호, 잡음이 있는 오디오 신호 및 잡음이 제거된 오디오 신호를 플로팅합니다. t = (1/fs)*(0:numel(denoisedAudioFullyConnected)-1); figure(3) tiledlayout(4,1) nexttile plot(t,cleanAudio(1:numel(denoisedAudioFullyConnected))) title("Clean Speech") grid on nexttile plot(t,noisyAudio(1:numel(denoisedAudioFullyConnected))) title("Noisy Speech") grid on nexttile plot(t,denoisedAudioFullyConnected) title("Denoised Speech (Fully Connected Layers)") grid on nexttile plot(t,denoisedAudioFullyConvolutional) title("Denoised Speech (Convolutional Layers)") grid on xlabel("Time (s)") 깨끗한 스펙트로그램, 잡음이 있는 스펙트로그램 및 잡음이 제거된 스펙트로그램을 플로팅합니다. h = figure(4); tiledlayout(4,1) nexttile spectrogram(cleanAudio,win,overlap,fftLength,fs); title("Clean Speech") grid on nexttile spectrogram(noisyAudio,win,overlap,fftLength,fs); title("Noisy Speech") grid on nexttile spectrogram(denoisedAudioFullyConnected,win,overlap,fftLength,fs); title("Denoised Speech (Fully Connected Layers)") grid on nexttile spectrogram(denoisedAudioFullyConvolutional,win,overlap,fftLength,fs); title("Denoised Speech (Convolutional Layers)") grid on p = get(h,"Position"); set(h,"Position",[p(1) 65 p(3) 800]); 잡음이 있는 음성을 들어봅니다. 완전 연결 계층을 갖는 신경망에서 잡음이 제거된 음성을 들어봅니다. sound(denoisedAudioFullyConnected,fs) 컨벌루션 계층을 갖는 신경망에서 잡음이 제거된 음성을 들어봅니다. sound(denoisedAudioFullyConvolutional,fs) 깨끗한 음성을 들어봅니다.
[cleanAudio,noisyAudio,denoisedAudioFullyConnected,denoisedAudioFullyConvolutional] = testDenoisingNets(adsTest,denoiseNetFullyConnected,denoiseNetFullyConvolutional,noisyMean,noisyStd,cleanMean,cleanStd); 실시간 활용 사례이전 섹션의 절차에서는 잡음이 있는 신호의 전체 스펙트럼을 잡음 제거 신경망의 실시간 스트리밍 버전을 시뮬레이션하는 방법을 보여주는 예제는
스코프는 깨끗한 신호, 잡음이 있는 신호 및 잡음이 제거된 신호와 잡음 게이트의 이득을 플로팅합니다. 참고 문헌[1] https://voice.mozilla.org/en [2] "Experiments on Deep Learning for Speech Denoising", Ding Liu, Paris Smaragdis, Minje Kim, INTERSPEECH, 2014. [3] "A Fully Convolutional Neural Network for Speech Enhancement", Se Rim Park, Jin Won Lee, INTERSPEECH, 2017. 참고 항목함수
관련 항목
|