For AI agents: the complete documentation index is available at https://docs.ovhcloud.com/fr/llms.txt, the full documentation bundle is available at https://docs.ovhcloud.com/fr/llms-full.txt, and this page is available as Markdown at https://docs.ovhcloud.com/fr/guides/public-cloud/ai-machine-learning/ai-deploy-streamlit-sounds-classification.md.

AI Deploy - Tutoriel - Déployer une app de classification audio grâce à Streamlit

Voir en Markdown

Comment déployer une app Streamlit pour classifier les sons des mammifères marins

Info

AI Deploy est couvert par les Conditions particulières OVHcloud Public Cloud.

Objectif

L'objectif de ce tutoriel est de déployer une application permettant de classifier des sons à partir d'un modèle pré-entraîné.

Le cas d'usage porte sur les sons de mammifères marins, un sujet déjà traité dans un précédent tutoriel.

Pour cela, vous allez utiliser Streamlit, un framework Python qui transforme des scripts en application web partageable. Vous apprendrez également à construire et à utiliser une image Docker personnalisée pour une application Streamlit.

Aperçu de l'app :

Aperçu

Prérequis

  • Un projet AI Deploy créé au sein d'un projet Public Cloud dans votre compte OVHcloud ;
  • Un utilisateur pour AI Deploy ;
  • Docker installé sur votre ordinateur local ;
  • Quelques connaissances sur la construction d'images et les Dockerfile ;
  • Vous devez également avoir suivi les étapes du notebook de classification des sons de mammifères marins du dépôt GitHub. Vous pourrez ainsi obtenir votre fichier data.csv et sauvegarder votre modèle.

En pratique

Vous allez suivre différentes étapes pour construire votre application Streamlit.

  • Plus d'informations sur les capacités de Streamlit sont disponibles ici.
  • Le lien direct vers le fichier python complet est disponible ici.

Écrire l'application Streamlit

Créez un fichier Python nommé app.py.

À l'intérieur de ce fichier, importez les modules requis.

import streamlit as st
import librosa
import csv
import os
import numpy as np
import pandas as pd
from PIL import Image
from keras.models import load_model
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import LabelEncoder

Créez la fonction qui permet de sauvegarder les sons envoyés dans un Object Container.

def save_file(sound_file):
    # save your sound file in the right folder by following the path
    with open(os.path.join('audio_files/', sound_file.name),'wb') as f:
         f.write(sound_file.getbuffer())
    return sound_file.name

Créez la fonction qui permet de transformer chaque son en fichier csv.

def transform_wav_to_csv(sound_saved):
    # define the column names
    header_test = 'filename length chroma_stft_mean chroma_stft_var rms_mean rms_var spectral_centroid_mean spectral_centroid_var spectral_bandwidth_mean \
        spectral_bandwidth_var rolloff_mean rolloff_var zero_crossing_rate_mean zero_crossing_rate_var harmony_mean harmony_var perceptr_mean perceptr_var tempo mfcc1_mean mfcc1_var mfcc2_mean \
        mfcc2_var mfcc3_mean mfcc3_var mfcc4_mean mfcc4_var'.split()
    # create the csv file
    file = open(f'csv_files/{os.path.splitext(sound_saved)[0]}.csv', 'w', newline = '')
    with file:
        writer = csv.writer(file)
        writer.writerow(header_test)      
    # calculate the value of the librosa parameters
    sound_name = f'audio_files/{sound_saved}'
    y, sr = librosa.load(sound_name, mono = True, duration = 30)
    chroma_stft = librosa.feature.chroma_stft(y = y, sr = sr)
    rmse = librosa.feature.rms(y = y)
    spec_cent = librosa.feature.spectral_centroid(y = y, sr = sr)
    spec_bw = librosa.feature.spectral_bandwidth(y = y, sr = sr)
    rolloff = librosa.feature.spectral_rolloff(y = y, sr = sr)
    zcr = librosa.feature.zero_crossing_rate(y)
    mfcc = librosa.feature.mfcc(y = y, sr = sr)
    to_append = f'{os.path.basename(sound_name)} {np.mean(chroma_stft)} {np.mean(rmse)} {np.mean(spec_cent)} {np.mean(spec_bw)} {np.mean(rolloff)} {np.mean(zcr)}'
    for e in mfcc:
        to_append += f' {np.mean(e)}'
    # fill in the csv file
    file = open(f'csv_files/{os.path.splitext(sound_saved)[0]}.csv', 'a', newline = '')
    with file:
        writer = csv.writer(file)
        writer.writerow(to_append.split())
    # create test dataframe
    df_test = pd.read_csv(f'csv_files/{os.path.splitext(sound_saved)[0]}.csv')
    # each time you add a sound, a line is added to the test.csv file
    # if you want to display the whole dataframe, you can deselect the following line
    #st.write(df_test)
    return df_test

Définissez la fonction qui classifie les sons à partir du modèle précédemment entraîné.

Info

Pour en savoir plus sur la façon de sauvegarder un modèle avec TensorFlow, reportez-vous à l'étape 8 "Save the model for future inference" du notebook.

Info

Ici, vous allez utiliser votre modèle entraîné, puis le sauvegarder dans un Object Container. Cliquez ici pour en savoir plus sur Object Storage.

def classification(dataframe):
    # create a dataframe with the csv file of the data used for training and validation
    df = pd.read_csv('csv_files/data.csv')
    # OUTPUT: labels => last column
    labels_list = df.iloc[:,-1]
    # encode the labels (0 => 44)
    converter = LabelEncoder()
    y = converter.fit_transform(labels_list)
    # INPUTS: all other columns are inputs except the filename
    scaler = StandardScaler()
    X = scaler.fit_transform(np.array(df.iloc[:, 1:27]))
    X_test = scaler.transform(np.array(dataframe.iloc[:, 1:27]))
    # load the pretrained model
    model = load_model('saved_model/my_model')
    # generate predictions for test samples
    predictions = model.predict(X_test)
    # generate argmax for predictions
    classes = np.argmax(predictions, axis = 1)
    # transform class number into class name
    result = converter.inverse_transform(classes)
    return result

Dans la barre latérale, si vous avez choisi Prediction, vous accédez à la fonction choice_prediction.

Elle vous permet d'envoyer un fichier son, de le convertir en fichier csv et d'obtenir le résultat de la classification en appelant les fonctions définies précédemment.

def choice_prediction():
    st.write('# Prediction')
    st.write('### Choose a marine mammal sound file in .wav format')
    # upload sound
    uploaded_file = st.file_uploader(' ', type='wav')
    if uploaded_file is not None:  
        # view details
        file_details = {'filename':uploaded_file.name, 'filetype':uploaded_file.type, 'filesize':uploaded_file.size}
        st.write(file_details)
        # read and play the audio file
        st.write('### Play audio')
        audio_bytes = uploaded_file.read()
        st.audio(audio_bytes, format='audio/wav')
        # save_file function
        save_file(uploaded_file)
        # define the filename
        sound = uploaded_file.name
        # transform_wav_to_csv function
        transform_wav_to_csv(sound)
        st.write('### Classification results')
        # if you select the predict button
        if st.button('Predict'):
            # write the prediction: the prediction of the last sound sent corresponds to the first column
            st.write("The marine mammal is: ",  str(classification(transform_wav_to_csv(sound))).replace('[', '').replace(']', '').replace("'", '').replace('"', ''))
    else:
        st.write('The file has not been uploaded yet')
    return

Il ne reste plus qu'à définir le main, où sont créées la barre latérale et la page d'accueil.

if __name__ == '__main__':
    st.image(Image.open('logo_ovh.png'), width=200)
    st.write('___')
    # create a sidebar
    st.sidebar.title('Marine mammal sounds classification')
    select = st.sidebar.selectbox('', ['Marine mammals', 'Prediction'], key='1')
    st.sidebar.write(select)
    # if sidebar selection is "Prediction"
    if select=='Prediction':
        # choice_prediction function
        choice_prediction()
    # else: stay on the home page
    else:
        st.write('# Marine mammals')
        st.write('The different marine mammals studied are the following.')
        st.write('For more information, please refer to this [link](https://cis.whoi.edu/science/B/whalesounds/index.cfm).')
        st.image(Image.open('marine_mammal_animals.png'))

Écrire le fichier requirements.txt de l'application

Le fichier requirements.txt nous permettra d'indiquer tous les modules nécessaires au fonctionnement de notre application. Ce fichier sera utile lors de l'écriture du Dockerfile.

streamlit==1.20.*
tensorflow==2.12.0
numpy==1.22.4
pandas==1.5.3
scikit-learn==1.2.*
librosa==0.10.*

Nous allons ici principalement voir comment écrire le code app.py, le fichier requirements.txt et le Dockerfile. Si vous souhaitez voir le code complet, reportez-vous au dépôt GitHub.

Écrire le Dockerfile de l'application

Votre Dockerfile doit commencer par l'instruction FROM indiquant l'image parente à utiliser. Dans notre cas, nous choisissons de partir de l'image python:3.8 :

python:3.8

Créez le répertoire personnel et ajoutez-y vos fichiers :

WORKDIR /workspace
ADD . /workspace

Installez le fichier requirements.txt qui contient les modules Python nécessaires à l'aide d'une commande pip install ... :

RUN pip install -r requirements.txt

Définissez votre commande de lancement par défaut pour démarrer l'application :

CMD [ "streamlit" , "run" , "/workspace/app.py", "--server.address=0.0.0.0" ]

Attribuez les droits d'accès corrects à l'utilisateur ovhcloud (42420:42420) :

RUN chown -R 42420:42420 /workspace
ENV HOME=/workspace

Construire l'image Docker depuis le Dockerfile

Depuis le répertoire contenant votre Dockerfile, exécutez l'une des commandes suivantes pour construire l'image de votre application :

# Build the image using your machine's default architecture
docker build . -t streamlit_app:latest

# Build image targeting the linux/amd64 architecture
docker buildx build --platform linux/amd64 -t streamlit_app:latest .
  • La première commande construit l'image en utilisant l'architecture par défaut de votre système. Cela peut fonctionner si votre machine utilise déjà l'architecture linux/amd64, qui est requise pour exécuter des containers avec nos produits AI. Cependant, sur des systèmes avec une architecture différente (par exemple ARM64 sur Apple Silicon), l'image résultante ne sera pas compatible et ne pourra pas être déployée.

  • La seconde commande cible explicitement l'architecture linux/amd64 afin de garantir la compatibilité avec nos services AI. Elle nécessite buildx, qui n'est pas installé par défaut. Si vous n'avez jamais utilisé buildx auparavant, vous pouvez l'installer en exécutant : docker buildx install

Info

L'argument point . indique que votre contexte de build (emplacement du Dockerfile et des autres fichiers nécessaires) est le répertoire courant.

L'argument -t vous permet de choisir l'identifiant à donner à votre image. Les identifiants d'image sont généralement composés d'un nom et d'un tag de version <name>:<version>. Pour cet exemple, nous avons choisi streamlit_app:latest.

Tester en local (optionnel)

Lancez la commande Docker suivante pour lancer votre application localement sur votre ordinateur :

docker run --rm -it -p 8501:8501 --user=42420:42420 streamlit_app:latest
Info

L'argument -p 8501:8501 indique que vous souhaitez exécuter une redirection de port depuis le port 8501 de votre machine locale vers le port 8501 du container Docker. Le port 8501 est le port par défaut utilisé par les applications Streamlit.

Warning

N'oubliez pas l'argument --user=42420:42420 si vous souhaitez simuler exactement le même comportement que celui qui se produira sur les apps AI Deploy. Il exécute le container Docker en tant qu'utilisateur OVHcloud spécifique (utilisateur 42420:42420).

Une fois démarrée, votre application devrait être disponible sur http://localhost:8501.

Envoyer l'image vers le registre partagé

Warning

Le registre partagé ne doit être utilisé qu'à des fins de test. Pensez à créer et à rattacher votre propre registre. Plus d'informations à ce sujet sont disponibles ici. Les images envoyées vers ce registre sont réservées aux workloads AI Tools et ne seront pas accessibles pour des usages externes.

Trouvez l'adresse de votre registre partagé en lançant cette commande :

ovhai registry list

Connectez-vous au registre partagé avec vos identifiants habituels d'utilisateur AI Platform :

docker login -u <user> -p <password> <shared-registry-address>

Envoyez l'image compilée vers le registre partagé :

docker tag streamlit_app:latest <shared-registry-address>/streamlit_app:latest
docker push <shared-registry-address>/streamlit_app:latest

Lancer l'app AI Deploy

La commande suivante démarre une nouvelle app exécutant votre application Streamlit :

ovhai app run --default-http-port 8501 \
      --cpu 1 \
      --volume <my_csv_files>@<region>/:/workspace/csv_files:RW \
      --volume <my_audio_files>@<region>/:/workspace/audio_files:RW \
      --volume <my_saved_model>@<region>/:/workspace/saved_model:RO \
      <shared-registry-address>/streamlit_app:latest
Info

--default-http-port 8501 indique que le port à atteindre sur l'URL de l'app est le 8501.

--cpu 1 indique que nous demandons 1 CPU pour cette app.

Pour lancer votre app Streamlit, vous devez attacher 3 volumes à votre app.

Le premier volume contient les fichiers csv issus de la transformation des fichiers son. Au lancement de l'app, cet Object Container ne contient que le fichier data.csv créé à l'étape 4 "Data preprocessing" du notebook.

Info

--volume <my_csv_files>@<region>/:/workspace/csv_files:RW est le premier volume attaché pour stocker les fichiers csv. Ce volume est en lecture/écriture (RW) car de nouveaux fichiers csv seront créés et sauvegardés à chaque envoi d'un nouveau son.

Le second volume contient les fichiers son que vous enverrez directement depuis l'app Streamlit. Au lancement de l'app, cet Object Container est vide. Il se remplira au fur et à mesure de l'envoi des fichiers son.

Info

--volume <my_audio_files>@<region>/:/workspace/audio_files:RW est le second volume attaché pour stocker les fichiers audio. Ce volume est attaché en lecture/écriture (RW) car de nouveaux fichiers audio y seront sauvegardés.

Le troisième conteneur contient le modèle que vous avez entraîné précédemment à l'étape 8 "Save the model for future inference" du notebook.

Info

--volume <my_saved_model>@<region>/:/workspace/saved_model:RO est le troisième volume attaché pour utiliser votre modèle pré-entraîné. Ce volume est en lecture seule (RO) car vous avez seulement besoin d'utiliser le modèle, sans apporter de modifications à cet Object Container.

Si vous souhaitez que votre notebook soit accessible sans avoir besoin de vous authentifier, spécifiez-le comme suit.

Info

Pensez à ajouter l'attribut --unsecure-http si vous souhaitez que votre application soit accessible sans authentification.

Aller plus loin

  • Vous pouvez imaginer déployer un modèle d'IA avec un autre outil : Gradio. Reportez-vous à ce tutoriel.
  • Une autre façon de créer une app AI Deploy est d'utiliser Flask ! C'est par ici.

Pour une formation ou une assistance technique sur la mise en œuvre de nos solutions, contactez votre commercial ou consultez la page Professional Services pour obtenir un devis et faire analyser votre projet par nos experts.

Votre avis nous intéresse !

N'hésitez pas à nous faire part de vos questions, retours et suggestions pour améliorer le service :

Cette page vous a-t-elle aidé ?