Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
tensorflow
GitHub Repository: tensorflow/docs-l10n
Path: blob/master/site/pt-br/tutorials/audio/transfer_learning_audio.ipynb
25118 views
Kernel: Python 3
#@title Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License.

Aprendizado por transferência com a YAMNet para classificação de sons do ambiente

A YAMNet é uma rede neural profunda pré-treinada que pode prever eventos de áudio de 521 classes, como risada, latido ou sirene.

Neste tutorial, você aprenderá a:

  • Carregar e usar o modelo YAMNet para inferência.

  • Criar um novo modelo usando os embeddings da YAMNet para classificar sons de gatos e cães.

  • Avaliar e exportar seu modelo.

Importe o TensorFlow e outras bibliotecas

Para começar, instale o TensorFlow I/O, que facilitará o carregamento dos arquivos de áudio fora do disco.

!pip install -q "tensorflow==2.11.*" # tensorflow_io 0.28 is compatible with TensorFlow 2.11 !pip install -q "tensorflow_io==0.28.*"
import os from IPython import display import matplotlib.pyplot as plt import numpy as np import pandas as pd import tensorflow as tf import tensorflow_hub as hub import tensorflow_io as tfio

Sobre a YAMNet

A YAMNet é uma rede neural pré-treinada que usa a arquitetura de convolução separável em profundidade MobileNetV1. Ela consegue usar uma forma de onda de áudio como entrada e fazer previsões independentes para cada um dos 521 eventos de áudio do corpus do AudioSet.

Internamente, o modelo extrai "frames" do sinal de áudio e processa lotes desses frames. Essa versão do modelo usa frames com duração de 0,96 segundo e extrai um frame a cada 0,48 segundo.

O modelo aceita um Tensor 1-D float32 ou um array do NumPy com uma forma de onda de duração arbitrária, representada como amostras de 16 kHz em canal único (mono) no intervalo [-1.0, +1.0]. Este tutorial contém código para ajudar você a converter arquivos WAV para o formato compatível.

O modelo retorna 3 saídas, incluindo as pontuações das classes, os embeddings (que você usará para o aprendizado por transferência) e o espectrograma log mel. Confira mais detalhes aqui.

Um uso específico da YAMNet é como um extrator de características de alto nível — a saída de embedding 1.024-dimensional. Você usará as características de entrada do modelo (YAMNet) base para alimentar o modelo mais superficial, que consiste em uma camada tf.keras.layers.Dense oculta. Em seguida, você treinará a rede com uma pequena quantidade de dados para a classificação de áudio sem precisar de vários dados rotulados e treinamento de ponta a ponta. (Isso é parecido com o aprendizado por transferência para a classificação de imagens com o TensorFlow Hub, se você quiser saber mais.)

Primeiro, você testará o modelo e verá os resultados da classificação de áudio. Depois, você construirá o pipeline de pré-processamento dos dados.

Carregando a YAMNet do TensorFlow Hub

Você usará a YAMNet pré-treinada do TensorFlow Hub para extrair os embeddings dos arquivos de som.

Para carregar um modelo do TensorFlow Hub, é simples: escolha o modelo, copie a URL dele e use a função load.

Observação: para ler a documentação do modelo, use a URL dele no seu navegador.

yamnet_model_handle = 'https://tfhub.dev/google/yamnet/1' yamnet_model = hub.load(yamnet_model_handle)

Com o modelo carregado, siga o tutorial de uso básico da YAMNet e baixe um arquivo WAV de amostra para realizar a inferência.

testing_wav_file_name = tf.keras.utils.get_file('miaow_16k.wav', 'https://storage.googleapis.com/audioset/miaow_16k.wav', cache_dir='./', cache_subdir='test_data') print(testing_wav_file_name)

Você precisará de uma função para carregar arquivos de áudio, que também será usada mais tarde ao trabalhar com os dados de treinamento. (Saiba mais sobre a leitura dos arquivos de áudio e dos rótulos deles em Reconhecimento de áudio simples.)

Observação: o wav_data retornado do load_wav_16k_mono já está normalizado para os valores no intervalo [-1.0, 1.0] (para mais informações, acesse a documentação da YAMNet no TF Hub).

# Utility functions for loading audio files and making sure the sample rate is correct. @tf.function def load_wav_16k_mono(filename): """ Load a WAV file, convert it to a float tensor, resample to 16 kHz single-channel audio. """ file_contents = tf.io.read_file(filename) wav, sample_rate = tf.audio.decode_wav( file_contents, desired_channels=1) wav = tf.squeeze(wav, axis=-1) sample_rate = tf.cast(sample_rate, dtype=tf.int64) wav = tfio.audio.resample(wav, rate_in=sample_rate, rate_out=16000) return wav
testing_wav_data = load_wav_16k_mono(testing_wav_file_name) _ = plt.plot(testing_wav_data) # Play the audio file. display.Audio(testing_wav_data, rate=16000)

Carregue o mapeamento das classes

É importante carregar os nomes das classes que a YAMNet consegue reconhecer. O arquivo de mapeamento está em yamnet_model.class_map_path() no formato CSV.

class_map_path = yamnet_model.class_map_path().numpy().decode('utf-8') class_names =list(pd.read_csv(class_map_path)['display_name']) for name in class_names[:20]: print(name) print('...')

Realize a inferência

A YAMNet oferece pontuações de classe no nível do frame (ou seja, 521 pontuações para cada frame). Para determinar as previsões no nível do clipe, as pontuações podem ser agregadas por classe nos frames (por exemplo, usando a agregação média ou máxima). Isso é realizado abaixo por scores_np.mean(axis=0). Por fim, para encontrar a classe com a maior pontuação no nível do clipe, determine o máximo das 521 pontuações agregadas.

scores, embeddings, spectrogram = yamnet_model(testing_wav_data) class_scores = tf.reduce_mean(scores, axis=0) top_class = tf.math.argmax(class_scores) inferred_class = class_names[top_class] print(f'The main sound is: {inferred_class}') print(f'The embeddings shape: {embeddings.shape}')

Observação: o modelo inferiu corretamente o som de um animal. Seu objetivo neste tutorial é aumentar a eficácia do modelo para classes específicas. Além disso, observe que o modelo gerou 13 embeddings, um por frame.

Dataset ESC-50

O dataset ESC-50 (Piczak, 2015) é uma coleção rotulada de 2 mil gravações de áudio do ambiente com 5 segundos de duração. O dataset consiste em 50 classes, com 40 exemplos por classe.

Baixe e extraia o dataset.

_ = tf.keras.utils.get_file('esc-50.zip', 'https://github.com/karoldvl/ESC-50/archive/master.zip', cache_dir='./', cache_subdir='datasets', extract=True)

Explore os dados

Os metadados para cada arquivo estão especificados no arquivo CSV em ./datasets/ESC-50-master/meta/esc50.csv

e todos os arquivos de áudio estão em ./datasets/ESC-50-master/audio/

Você criará um DataFrame do pandas com o mapeamento e usará isso para ter uma visão mais clara dos dados.

esc50_csv = './datasets/ESC-50-master/meta/esc50.csv' base_data_path = './datasets/ESC-50-master/audio/' pd_data = pd.read_csv(esc50_csv) pd_data.head()

Filtre os dados

Agora que os dados estão armazenados no DataFrame, aplique algumas transformações:

  • Filtre as linhas e use apenas as classes selecionadas – dog e cat. Se você quiser usar outras classes, é aqui que elas devem ser escolhidas.

  • Altere o nome do arquivo para incluir o caminho completo. Isso facilitará o carregamento depois.

  • Mude os alvos para um determinado intervalo. Neste exemplo, dog permanecerá 0, mas cat será 1 em vez do valor original 5.

my_classes = ['dog', 'cat'] map_class_to_id = {'dog':0, 'cat':1} filtered_pd = pd_data[pd_data.category.isin(my_classes)] class_id = filtered_pd['category'].apply(lambda name: map_class_to_id[name]) filtered_pd = filtered_pd.assign(target=class_id) full_path = filtered_pd['filename'].apply(lambda row: os.path.join(base_data_path, row)) filtered_pd = filtered_pd.assign(filename=full_path) filtered_pd.head(10)

Carregue os arquivos de áudio e recupere os embeddings

Aqui, você aplicará o load_wav_16k_mono e preparará os dados WAV para o modelo.

Ao extrair embeddings dos dados WAV, você obterá um array de formato (N, 1024), em que N é o número de frames encontrados pela YAMNet (um para cada 0,48 segundo de áudio).

Seu modelo usará cada frame como uma entrada. Portanto, você precisa criar uma nova coluna com um frame por linha. Você também precisa expandir os rótulos e a coluna fold para refletir essas novas linhas de maneira adequada.

A coluna fold expandida mantém os valores originais. Não é possível misturar frames porque, ao fazer as divisões, você pode acabar com partes do mesmo áudio em conjuntos diferentes, tornando os passos de teste e validação menos eficazes.

filenames = filtered_pd['filename'] targets = filtered_pd['target'] folds = filtered_pd['fold'] main_ds = tf.data.Dataset.from_tensor_slices((filenames, targets, folds)) main_ds.element_spec
def load_wav_for_map(filename, label, fold): return load_wav_16k_mono(filename), label, fold main_ds = main_ds.map(load_wav_for_map) main_ds.element_spec
# applies the embedding extraction model to a wav data def extract_embedding(wav_data, label, fold): ''' run YAMNet to extract embedding from the wav data ''' scores, embeddings, spectrogram = yamnet_model(wav_data) num_embeddings = tf.shape(embeddings)[0] return (embeddings, tf.repeat(label, num_embeddings), tf.repeat(fold, num_embeddings)) # extract embedding main_ds = main_ds.map(extract_embedding).unbatch() main_ds.element_spec

Divida os dados

Você usará a coluna fold para dividir o dataset em conjuntos de treinamento, validação e teste.

O ESC-50 é disposto em cinco folds de validação cruzada com tamanho uniforme. Assim, os clipes da mesma fonte original estão sempre no mesmo fold — saiba mais no documento ESC: Dataset for Environmental Sound Classification (Dataset para a classificação de sons do ambiente).

A última etapa é remover a coluna fold do dataset, já que ela não será usada durante o treinamento.

cached_ds = main_ds.cache() train_ds = cached_ds.filter(lambda embedding, label, fold: fold < 4) val_ds = cached_ds.filter(lambda embedding, label, fold: fold == 4) test_ds = cached_ds.filter(lambda embedding, label, fold: fold == 5) # remove the folds column now that it's not needed anymore remove_fold_column = lambda embedding, label, fold: (embedding, label) train_ds = train_ds.map(remove_fold_column) val_ds = val_ds.map(remove_fold_column) test_ds = test_ds.map(remove_fold_column) train_ds = train_ds.cache().shuffle(1000).batch(32).prefetch(tf.data.AUTOTUNE) val_ds = val_ds.cache().batch(32).prefetch(tf.data.AUTOTUNE) test_ds = test_ds.cache().batch(32).prefetch(tf.data.AUTOTUNE)

Crie seu modelo

Você já fez a maior parte do trabalho! Em seguida, defina um modelo Sequential bastante simples com uma camada oculta e duas saídas para reconhecer gatos e cães nos sons.

my_model = tf.keras.Sequential([ tf.keras.layers.Input(shape=(1024), dtype=tf.float32, name='input_embedding'), tf.keras.layers.Dense(512, activation='relu'), tf.keras.layers.Dense(len(my_classes)) ], name='my_model') my_model.summary()
my_model.compile(loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True), optimizer="adam", metrics=['accuracy']) callback = tf.keras.callbacks.EarlyStopping(monitor='loss', patience=3, restore_best_weights=True)
history = my_model.fit(train_ds, epochs=20, validation_data=val_ds, callbacks=callback)

Vamos executar o método evaluate nos dados de teste só para conferir se não há overfitting.

loss, accuracy = my_model.evaluate(test_ds) print("Loss: ", loss) print("Accuracy: ", accuracy)

Você conseguiu!

Teste seu modelo

Em seguida, teste seu modelo no embedding do teste anterior usando apenas a YAMNet.

scores, embeddings, spectrogram = yamnet_model(testing_wav_data) result = my_model(embeddings).numpy() inferred_class = my_classes[result.mean(axis=0).argmax()] print(f'The main sound is: {inferred_class}')

Salve um modelo que aceite diretamente um arquivo WAV como entrada

Seu modelo funciona ao fornecer embeddings como entrada.

Em um cenário real, você usará dados de áudio como entrada direta.

Para fazer isso, você combinará a YAMNet e o seu modelo em um único modelo que possa ser exportado para outros aplicativos.

Para facilitar o uso do resultado do modelo, a camada final será uma operação reduce_mean. Ao usar esse modelo para implantação (que você aprenderá depois no tutorial), será necessário o nome da camada final. Se você não definir um, o TensorFlow definirá automaticamente um nome incremental que dificulta o teste, já que mudará sempre que você treinar o modelo. Ao usar uma operação do TensorFlow pura, não é possível atribuir um nome. Para resolver esse problema, crie uma camada personalizada que aplique reduce_mean e chame-a de 'classifier'.

class ReduceMeanLayer(tf.keras.layers.Layer): def __init__(self, axis=0, **kwargs): super(ReduceMeanLayer, self).__init__(**kwargs) self.axis = axis def call(self, input): return tf.math.reduce_mean(input, axis=self.axis)
saved_model_path = './dogs_and_cats_yamnet' input_segment = tf.keras.layers.Input(shape=(), dtype=tf.float32, name='audio') embedding_extraction_layer = hub.KerasLayer(yamnet_model_handle, trainable=False, name='yamnet') _, embeddings_output, _ = embedding_extraction_layer(input_segment) serving_outputs = my_model(embeddings_output) serving_outputs = ReduceMeanLayer(axis=0, name='classifier')(serving_outputs) serving_model = tf.keras.Model(input_segment, serving_outputs) serving_model.save(saved_model_path, include_optimizer=False)
tf.keras.utils.plot_model(serving_model)

Carregue seu modelo salvo para verificar se ele funciona conforme esperado.

reloaded_model = tf.saved_model.load(saved_model_path)

E para o teste final: ao fornecer alguns dados de som, seu modelo retorna o resultado correto?

reloaded_results = reloaded_model(testing_wav_data) cat_or_dog = my_classes[tf.math.argmax(reloaded_results)] print(f'The main sound is: {cat_or_dog}')

Se você quiser testar seu novo modelo em uma configuração de implantação, use a assinatura 'serving_default'.

serving_results = reloaded_model.signatures['serving_default'](testing_wav_data) cat_or_dog = my_classes[tf.math.argmax(serving_results['classifier'])] print(f'The main sound is: {cat_or_dog}')

(Opcional) Mais testes

O modelo está pronto.

Vamos compará-lo a YAMNet no dataset de teste.

test_pd = filtered_pd.loc[filtered_pd['fold'] == 5] row = test_pd.sample(1) filename = row['filename'].item() print(filename) waveform = load_wav_16k_mono(filename) print(f'Waveform values: {waveform}') _ = plt.plot(waveform) display.Audio(waveform, rate=16000)
# Run the model, check the output. scores, embeddings, spectrogram = yamnet_model(waveform) class_scores = tf.reduce_mean(scores, axis=0) top_class = tf.math.argmax(class_scores) inferred_class = class_names[top_class] top_score = class_scores[top_class] print(f'[YAMNet] The main sound is: {inferred_class} ({top_score})') reloaded_results = reloaded_model(waveform) your_top_class = tf.math.argmax(reloaded_results) your_inferred_class = my_classes[your_top_class] class_probabilities = tf.nn.softmax(reloaded_results, axis=-1) your_top_score = class_probabilities[your_top_class] print(f'[Your model] The main sound is: {your_inferred_class} ({your_top_score})')

Próximos passos

Você criou um modelo que consegue classificar sons de cães ou gatos. Com a mesma ideia e um dataset diferente, você pode tentar, por exemplo, criar um identificador acústico de pássaros com base nos seus cantos.

Compartilhe seu projeto com a equipe do TensorFlow nas redes sociais!