딥러닝 노이즈 캔슬링 - dibleoning noijeu kaenseulling

딥러닝과 노이즈캔슬링이 만났을 때

딥러닝 노이즈 캔슬링 - dibleoning noijeu kaenseulling

통화를 할 때 주위 잡음을 없애주는 노이즈 캔슬링은 편안한 대화를 위해 필수적이라고 할 수 있다. 사실 일상 자체는 수많은 주위 소음에 둘러싸여 있다. 시내나 공항에서 전화를 걸면 이 같은 주위 소음까지 상대방에게 들린다. 문제는 항상 완벽하게 소음을 차단해주는 제품을 찾기는 쉽지 않다는 것이다. 그런데 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건 동시 통화가 가능하게 됐다고 한다.

딥러닝 노이즈 캔슬링 - dibleoning noijeu kaenseulling

음성 전송이나 코딩 같은 기본 처리는 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

딥러닝 노이즈 캔슬링 - dibleoning noijeu kaenseulling

음성 잡음 제거의 목표는 음성 신호에서 세탁기 소음을 제거하는 한편 출력 신호에서 원치 않는 아티팩트를 최소화하는 것입니다.

데이터셋 검토하기

이 예제에서는 Mozilla Common Voice 데이터셋 [1]의 서브셋을 사용하여 딥러닝 신경망을 훈련시키고 테스트합니다. 데이터 세트에는 연구대상자가 말하는 짧은 문장을 48kHz로 녹음한 내용이 담겨 있습니다. 데이터 세트를 다운로드하고 다운로드한 파일의 압축을 풉니다.

downloadFolder = matlab.internal.examples.downloadSupportFile("audio","commonvoice.zip");
dataFolder = tempdir;
unzip(downloadFolder,dataFolder)
dataset = fullfile(dataFolder,"commonvoice");

audioDatastore를 사용하여 훈련 세트를 위한 데이터저장소를 만듭니다. 성능을 희생하여 예제 런타임의 속도를 높이려면 speedupExampletrue로 설정하십시오.

adsTrain = audioDatastore(fullfile(dataset,"train"),IncludeSubfolders=true);

speedupExample = 
딥러닝 노이즈 캔슬링 - dibleoning noijeu kaenseulling
true
; if speedupExample adsTrain = shuffle(adsTrain); adsTrain = subset(adsTrain,1:1000); end

read를 사용하여 데이터저장소의 첫 번째 파일의 내용을 가져옵니다.

[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

딥러닝 노이즈 캔슬링 - dibleoning noijeu kaenseulling

딥러닝 시스템 개요

아래에는 기본적인 딥러닝 훈련 도식이 나와 있습니다. 음성은 일반적으로 4kHz보다 낮으므로 먼저 깨끗한 오디오 신호와 잡음이 있는 오디오 신호를 8kHz로 다운샘플링하여 신경망의 계산 부하를 줄일 것입니다. 예측 변수 및 목표 신경망 신호는 각각 잡음이 있는 오디오 신호와 깨끗한 오디오 신호의 크기 스펙트럼입니다. 신경망 출력값은 잡음이 제거된 신호의 크기 스펙트럼입니다. 회귀 신경망은 예측 변수 입력값을 사용하여 출력 목표와 입력 목표 사이의 평균 제곱 오차를 최소화합니다. 잡음이 제거된 오디오는 출력 크기 스펙트럼과 잡음이 있는 신호의 위상을 사용하여 시간 영역으로 다시 변환됩니다 [2].

딥러닝 노이즈 캔슬링 - dibleoning noijeu kaenseulling

윈도우 길이 256개 샘플, 중첩 75% 및 해밍 윈도우를 갖는 단시간 푸리에 변환(STFT)을 사용하여 오디오를 주파수 영역으로 변환합니다. 음수 주파수에 해당하는 주파수 샘플을 제외하여 스펙트럼 벡터의 크기를 129로 줄입니다(시간 영역 음성 신호는 실수이므로 이는 정보 손실로 이어지지 않음). 예측 변수 입력값은 8개의 잡음이 있는 연속된 STFT 벡터로 구성되어 있어 각 STFT 출력 추정값은 잡음이 있는 현재 STFT 1개와 잡음이 있는 이전 STFT 벡터 7개를 바탕으로 계산됩니다.

딥러닝 노이즈 캔슬링 - dibleoning noijeu kaenseulling

STFT 목표값과 예측 변수

이 섹션에서는 하나의 훈련 파일에서 목표 신호와 예측 변수 신호를 생성하는 방법을 보여줍니다.

먼저 시스템 파라미터를 정의합니다.

windowLength = 256;
win = hamming(windowLength,"periodic");
overlap = round(0.75*windowLength);
fftLength = windowLength;
inputFs = 48e3;
fs = 8e3;
numFeatures = fftLength/2 + 1;
numSegments = 8;

dsp.SampleRateConverter (DSP System Toolbox) 객체를 만들어 48kHz 오디오를 8kHz로 변환합니다.

src = dsp.SampleRateConverter(InputSampleRate=inputFs,OutputSampleRate=fs,Bandwidth=7920);

read를 사용하여 데이터저장소에서 오디오 파일의 내용을 가져옵니다.

오디오 길이가 샘플 레이트 변환기 데시메이션 인자의 배수여야 합니다.

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;

stft (Signal Processing Toolbox)를 사용하여 원래 오디오 신호와 잡음이 있는 오디오 신호에서 크기 STFT 벡터를 생성합니다.

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형 배열은 일반적으로 gather 함수를 호출하기 전까지 계산되지 않은 상태로 유지됩니다. 이처럼 계산을 보류하여 대규모 데이터 세트에 대해 빠르게 작업할 수 있습니다. 이후 gather를 사용하여 출력값을 요청하면 MATLAB은 가능한 경우 대기 중인 계산을 결합하여 데이터 통과 횟수를 최소화합니다. Parallel Computing Toolbox™가 있으면 로컬 MATLAB 세션에서 또는 로컬 병렬 풀에서 tall형 배열을 사용할 수 있습니다. MATLAB® Parallel Server™가 설치되어 있으면 클러스터에서도 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형 배열 변수를 만듭니다. 함수 HelperGenerateSpeechDenoisingFeatures는 STFT 목표값과 예측 변수 섹션에서 이미 살펴본 단계를 수행합니다. cellfun 명령은 데이터저장소에 있는 각 오디오 파일의 내용에 HelperGenerateSpeechDenoisingFeatures를 적용합니다.

[targets,predictors] = cellfun(@(x)HelperGenerateSpeechDenoisingFeatures(x,noise,src),T,UniformOutput=false);

gather를 사용하여 목표값과 예측 변수를 계산합니다.

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

완전 연결 계층을 사용한 음성 잡음 제거

먼저 완전 연결 계층으로 구성된 잡음 제거 신경망을 살펴보겠습니다. 완전 연결 계층의 각 뉴런은 이전 계층의 모든 활성화에 연결됩니다. 완전 연결 계층은 입력값에 가중치 행렬을 곱한 다음 편향 벡터를 더합니다. 가중치 행렬과 편향 벡터의 차원은 계층의 뉴런 개수와 이전 계층의 활성화 개수로 결정됩니다.

딥러닝 노이즈 캔슬링 - dibleoning noijeu kaenseulling

신경망의 계층을 정의합니다. 입력 크기를 NumFeatures×NumSegments(이 예제에서는 129×8)인 영상이 되도록 지정합니다. 각각 1024개의 뉴런을 갖는 2개의 은닉 완전 연결 계층을 정의합니다. 순수 선형 시스템이므로 각 은닉 완전 연결 계층 뒤에 ReLU(Rectified Linear Unit) 계층이 오도록 합니다. 배치 정규화 계층은 출력값의 평균과 표준편차를 정규화합니다. 129개의 뉴런을 갖는 완전 연결 계층을 추가하고 그 뒤에 회귀 계층을 추가합니다.

layers = [
    imageInputLayer([numFeatures,numSegments])
    fullyConnectedLayer(1024)
    batchNormalizationLayer
    reluLayer
    fullyConnectedLayer(1024)
    batchNormalizationLayer
    reluLayer
    fullyConnectedLayer(numFeatures)
    regressionLayer
    ];

다음으로, 신경망의 훈련 옵션을 지정합니다. 신경망이 훈련 데이터를 3번 통과하도록 MaxEpochs3으로 설정합니다. 신경망이 한 번에 128개의 훈련 신호를 살펴보도록 MiniBatchSize128로 설정합니다. Plots"training-progress"로 지정하여 반복 횟수가 늘어남에 따라 훈련 진행 상황을 표시하는 플롯을 생성합니다. Verbosefalse로 설정하여 플롯에 표시되는 데이터에 대응되는 표의 형태로 출력값이 명령줄 창에 출력되지 않도록 합니다. Shuffle"every-epoch"로 지정하여 각 Epoch의 시작 시점에 훈련 시퀀스를 섞습니다. LearnRateSchedule"piecewise"로 지정하여 특정 횟수의 Epoch(1)를 통과할 때마다 지정된 인수(0.9)만큼 학습률을 줄입니다. ValidationData를 검증 예측 변수와 목표값으로 설정합니다. Epoch 1회마다 한 번씩 검증 평균 제곱 오차가 계산되도록 ValidationFrequency를 설정합니다. 이 예제에서는 ADAM(적응적 모멘트 추정) 솔버를 사용합니다.

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

trainNetwork를 사용하여 지정된 훈련 옵션과 계층 아키텍처로 신경망을 훈련시킵니다. 훈련 세트가 크기 때문에 훈련 과정에 몇 분 정도 걸릴 수 있습니다. 신경망을 처음부터 훈련시키는 대신 사전 훈련된 신경망을 다운로드하고 불러오려면 downloadPretrainedSystemtrue로 설정하십시오.

downloadPretrainedSystem = 
딥러닝 노이즈 캔슬링 - dibleoning noijeu kaenseulling
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])});

trainNetwork를 사용하여 지정된 훈련 옵션과 계층 아키텍처로 신경망을 훈련시킵니다. 훈련 세트가 크기 때문에 훈련 과정에 몇 분 정도 걸릴 수 있습니다. 신경망을 처음부터 훈련시키는 대신 사전 훈련된 신경망을 다운로드하고 불러오려면 downloadPretrainedSystemtrue로 설정하십시오.

downloadPretrainedSystem = 
딥러닝 노이즈 캔슬링 - dibleoning noijeu kaenseulling
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;

stft를 사용하여 잡음이 있는 오디오 신호에서 크기 STFT 벡터를 생성합니다.

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개의 훈련된 신경망으로 predict를 사용하여 잡음이 제거된 크기 STFT를 계산합니다.

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];

잡음이 제거된 음성 신호를 계산합니다. istft가 역 STFT를 수행합니다. 잡음이 있는 STFT 벡터의 위상을 사용하여 시간 영역 신호를 재생성합니다.

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)")

딥러닝 노이즈 캔슬링 - dibleoning noijeu kaenseulling

깨끗한 스펙트로그램, 잡음이 있는 스펙트로그램 및 잡음이 제거된 스펙트로그램을 플로팅합니다.

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

딥러닝 노이즈 캔슬링 - dibleoning noijeu kaenseulling

잡음이 있는 음성을 들어봅니다.

완전 연결 계층을 갖는 신경망에서 잡음이 제거된 음성을 들어봅니다.

sound(denoisedAudioFullyConnected,fs)

컨벌루션 계층을 갖는 신경망에서 잡음이 제거된 음성을 들어봅니다.

sound(denoisedAudioFullyConvolutional,fs)

깨끗한 음성을 들어봅니다.

testDenoisingNets를 호출하여 데이터저장소의 더 많은 파일을 테스트할 수 있습니다. 이 함수는 위에서 살펴본 시간 영역 플롯과 주파수 영역 플롯을 생성하고, 깨끗한 오디오 신호, 잡음이 있는 오디오 신호 및 잡음이 제거된 오디오 신호도 반환합니다.

[cleanAudio,noisyAudio,denoisedAudioFullyConnected,denoisedAudioFullyConvolutional] = testDenoisingNets(adsTest,denoiseNetFullyConnected,denoiseNetFullyConvolutional,noisyMean,noisyStd,cleanMean,cleanStd);

딥러닝 노이즈 캔슬링 - dibleoning noijeu kaenseulling

딥러닝 노이즈 캔슬링 - dibleoning noijeu kaenseulling

실시간 활용 사례

이전 섹션의 절차에서는 잡음이 있는 신호의 전체 스펙트럼을 predict로 전달했습니다. 이는 낮은 대기 시간이 요구되는 실시간 활용 사례에는 적합하지 않습니다.

잡음 제거 신경망의 실시간 스트리밍 버전을 시뮬레이션하는 방법을 보여주는 예제는 speechDenoisingRealtimeApp을 실행하십시오. 이 앱은 완전 연결 계층이 있는 신경망을 사용합니다. 오디오 프레임 길이는 STFT의 건너 뛰는 크기, 즉 0.25 * 256 = 64개 샘플과 동일합니다.

speechDenoisingRealtimeApp은 시뮬레이션과 상호 작용하도록 설계된 사용자 인터페이스(UI)를 실행합니다. 이 UI를 사용하여 파라미터를 조정할 수 있으며, 결과는 즉시 시뮬레이션에 반영됩니다. 잡음이 제거된 출력값에 작용하는 잡음 게이트를 활성화/비활성화하여 잡음을 더 줄이고 잡음 게이트의 어택 시간, 해제 시간 및 임계값을 조정할 수도 있습니다. UI에서 깨끗한 오디오, 잡음이 없는 오디오 또는 잡음이 제거된 오디오를 들어볼 수 있습니다.

딥러닝 노이즈 캔슬링 - dibleoning noijeu kaenseulling

스코프는 깨끗한 신호, 잡음이 있는 신호 및 잡음이 제거된 신호와 잡음 게이트의 이득을 플로팅합니다.

딥러닝 노이즈 캔슬링 - dibleoning noijeu kaenseulling

참고 문헌

[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.

참고 항목

함수

  • trainingOptions | trainNetwork

관련 항목

  • MATLAB의 딥러닝