CCINP Informatique Commune PSI 2020Sujet, corrigé et rapport du jury
Détection d’obstacles par un sonar de sous-marin
- Programmation en Python (listes, boucles, fonctions)
- Bibliothèque numpy
- Représentation des nombres en machine
- Lecture de fichiers et chaînes de caractères
- Intégration numérique
- Récursivité
- Arbres de décision
Téléchargements
Présentation du sujet
AccessibleDétection d'obstacles par le sonar d'un sous-marin : spectrogramme, arbres de décision et forêts aléatoiresAfficher ou masquer la section
Présentation du sujet
AccessibleLe sujet d'informatique CCINP PSI 2020 cherche à distinguer une roche d'un objet métallique à partir des mesures d'un sonar. Après une introduction, la partie II traite le signal (signal modulé en fréquence, transformée de Fourier locale, enveloppe normalisée, lecture d'un fichier de données) et la partie III construit des arbres de décision avec l'indice de Gini, puis une forêt aléatoire.
- 1Partie I : introductionPrésentation du problème de classification des obstacles et des algorithmes d'intelligence artificielle étudiés.
- 2Partie II : analyse des donnéesCréation du vecteur des instants et du signal émis, spectrogramme par transformée de Fourier locale, intégration numérique et normalisation de l'enveloppe, lecture d'un fichier et taille mémoire des données (Q1 à Q9).
- 3Partie III : méthode des forêts aléatoiresReprésentation d'un arbre de décision par des listes, séparation des données avec l'indice de Gini, construction récursive de l'arbre, puis prédiction par vote majoritaire sur plusieurs arbres (Q10 à Q20).
Accessible. Le jury indique que l'épreuve a été bien réussie par la majorité des candidats, avec peu d'erreurs de syntaxe Python.
L'épreuve en chiffres
Moyenne 10,11 / 20 · écart-type 4,16 · 5 085 présents · où vous situez-vous ?Afficher ou masquer la section
L'épreuve en chiffres
- Moyenne
- 10,11/ 20
- Écart-type
- 4,16
- Présents
- 5 085
- Coefficient
- 7
- Durée
- 3 h
Votre note sur 20 à ce sujet, en conditions de concours.
Source : document officiel du concours, épreuve du 4 juillet 2020. Notes publiées par le concours (après harmonisation le cas échéant). Courbe : estimation par une loi normale.
Ce qu'a observé le jury
6 erreurs relevéesConfusion entre arange et linspace · Initialisation du tableau renvoyé · Arguments de la fonction de transformée de FourierAfficher ou masquer la section
Ce qu'a observé le jury
6 erreurs relevéesL'épreuve parcourait une large part du programme d'informatique de CPGE et a été bien réussie par la majorité des candidats. Le document réponse, introduit cette session, a été globalement bien utilisé. Les erreurs récurrentes portent sur les indices décalés de un, les variables non définies, la représentation des nombres et la confusion entre listes et tableaux numpy.
Les erreurs les plus sanctionnées
- 1Confusion entre arange et linspaceQ1
La création du tableau des instants a souvent échoué : pas et nombre de valeurs confondus, dernière valeur incluse ou exclue mal gérée.
« confusion entre arange et linspace (pas versus nombre de valeurs, dernière valeur exclue versus incluse par défaut) »
- 2Initialisation du tableau renvoyéQ2
Des candidats ajoutent des éléments à un array vide ou ne remplissent pas toutes les cases d'un empty(N) ; le jury préfère zeros(). La définition du signal sur deux intervalles est aussi souvent oubliée.
- 3Arguments de la fonction de transformée de FourierQ3
Presque aucun candidat ne passe les bons arguments, en particulier le premier ; beaucoup appliquent la transformée au vecteur des temps.
« pratiquement aucun candidat ne met les bons arguments dans la fonction et notamment le premier argument »
- 4Normalisation et complexitéQ7
Peu de candidats trouvent la fonction affine à appliquer ; le calcul du minimum et du maximum laissé dans la boucle alourdit fortement la complexité.
- 5Taille mémoire d'un flottantQ9
La plupart ignorent la place occupée par un réel en double précision et confondent bits et octets.
« une grande majorité des candidats ne connaissent pas l'espace nécessaire pour stocker un réel en double précision »
- 6Récursivité et tirage sans répétitionQ12, Q16, Q18
Le test d'absence d'une valeur déjà tirée est souvent mal écrit alors que « in » suffisait ; dans la fonction récursive de prédiction, les return des appels récursifs sont souvent oubliés.
Ce qui a été bien réussi
- Peu d'erreurs de syntaxe Python ont été relevées.
- Les questions Q5, Q10, Q17 et Q20 sont bien traitées, ainsi que Q6 et Q13 dans l'ensemble.
- Les rares candidats ayant abordé Q19 l'ont en général bien réussie.
Conseils du jury
- Utiliser une encre d'une autre couleur que le noir pour compléter le code sur le document réponse.
- Préparer la réponse au brouillon, choisir des noms de variables explicites et soigner l'indentation.
- Ne pas confondre l'indice et l'élément dans une boucle for.
- Connaître les bases de la représentation des informations en mémoire, qui feront toujours l'objet de questions.
- Prendre le temps de comprendre ce que doit faire une fonction avant de la compléter.
Synthèse rédigée par WikiPrépa à partir du rapport officiel du jury (à télécharger en PDF). Les citations sont extraites du rapport.
Ces sujets peuvent vous intéresser
Lecture du sujet en ligne
L'énoncé complet, avec les formules et les figures, sans ouvrir le PDF.Afficher ou masquer la section
Lecture du sujet en ligne
ÉPREUVE MUTUALISÉE AVEC E3A-POLYTECH
ÉPREUVE SPÉCIFIQUE - FILIÈRE PSI
INFORMATIQUE
Mercredi 6 mai : 8 h-11 h
RAPPEL DES CONSIGNES
- Utiliser uniquement un stylo noir ou bleu foncé non effaçable pour la rédaction de votre composition ; d'autres couleurs, excepté le vert, peuvent être utilisées, mais exclusivement pour les schémas et la mise en évidence des résultats.
- Ne pas utiliser de correcteur.
- Écrire le mot FIN à la fin de votre composition.
Les calculatrices sont interdites
Le sujet est composé de trois parties indépendantes.
Les différents algorithmes doivent être rendus dans leur forme définitive sur le document réponse dans l'espace réservé à cet effet en respectant les éléments de syntaxe du langage (les brouillons ne sont pas acceptés).
La réponse ne doit pas se cantonner à la rédaction de l'algorithme sans explication, les programmes doivent être expliqués et commentés.
Document réponse et Annexe : page 1 à page 8
Détection d'obstacles par un sonar de sous-marin
Partie I - Introduction
Il existe des techniques d'aide à la décision faisant partie des algorithmes dits d'Intelligence Artificielle permettant d'analyser le signal de retour d'un sonar et de déterminer de quelle nature est l'obstacle.
Objectif
- analyse et représentation des données,
- construction des arbres de décisions,
- prédiction par la méthode «random forest».
Partie II - Analyse des données
II. 1 - Présentation
Le signal émis par le sonar et le signal reçu sont numériques et représentés avec

.jpg)
Description de l'algorithme de transformée de Fourier locale
Pour obtenir les intervalles de temps utilisés dans la méthode, on sélectionne un instant particulier (
En appliquant la transformée de Fourier (FFT) au signal obtenu, on extrait un vecteur de fréquences de taille
Finalement, on obtient alors une fonction discrète de deux variables

Description de la fonction stft
scipy.signal.stft(x, fs=1.0, window='hann', nperseg=256, noverlap=None)
Compute the Short Time Fourier Transform (STFT).
STFTs can be used as a way of quantifying the change of a nonstationary
signal's frequency and phase content over time.
Parameters:
x : array_like
Time series of measurement values.
fs : float
Sampling frequency of the x time series. This value is used to define
array of frequencies which length is equal to x length // (nperseg//2)
. Defaults to 1.0.
window : str
Desired window to use. If window is a string, it is passed to get_window
to generate the window values. Available windows are : boxcar, triang,
hamming, hann, kaiser... Defaults to a Hann window.
nperseg : int
Length of each window. Defaults to 256.
noverlap : int, optional
Number of points to overlap between segments.
If None, noverlap = nperseg // 2.
Returns:
f : ndarray
Array of sample frequencies.
t : ndarray
Array of segment times which length is equal to x length // (nperseg//2).
S : ndarray
STFT of x.
Les spectrogrammes obtenus sont donnés sur la figure 4.

On note
La fonction
De manière à pouvoir comparer les enveloppes, on normalise le vecteur
II. 2 - Lecture des données
Premières lignes partielles du fichier, les points de suspension permettent de cacher les 56 données intermédiaires pour chaque ligne :
def lire_donnees(nom_fichier):
donnees = []
with open (nom_fichier, 'r') as fichier
for ligne in fichier:
a = ligne.split(',')
b = []
for i in range(len(a)-1):
b.append(float(a[i]))
if a[-1].strip() == "R":
b.append(0.0)
else :
b.append(1.0)
donnees.append(b)
return donnees
Q9. Donner la taille mémoire minimale nécessaire en octets pour stocker les données. On rappelle que Python stocke les nombres réels en format double précision par défaut.
Partie III - Méthode des forêts aléatoires
III. 1 - Arbre de décision
Pour classer une nouvelle donnée, il suffit ensuite de suivre les différentes règles issues de la construction de l'arbre pour savoir dans quelle catégorie la placer.
Le principal problème rencontré est qu'un arbre de décision peut vite conduire à du surapprentissage si l'on tente de décrire parfaitement le jeu de données initial avec une donnée unique par feuille. Dans ce cas, il ne sera pas forcément possible de classer une nouvelle donnée. Il est donc souvent nécessaire d'arrêter la construction à un nombre maximal de séparations, on parle d'élaguer l'arbre.

.jpg)
En Python, on choisit de représenter un nœud par une liste de 4 éléments [ind, val, gauche, droite]. Le premier élément est l'indice de la colonne du tableau de données, le deuxième élément est la valeur permettant de faire le test pour descendre à droite ou à gauche. Le troisième élément est :
- soit le nœud de la branche de gauche (représentée elle-même par une structure de type nœud)
- soit la valeur terminale 0 ou 1 pour définir le groupe.
Par exemple, l'arbre de la figure 5(b) sera représenté en Python par la liste suivante :
Q10. Donner la représentation en Python de l'arbre défini sur la figure 5(a).
Soit une donnée non classée
Q11. Déterminer, en justifiant, dans quel groupe cette donnée sera classée en utilisant l'arbre de la figure 5(a). Expliquer le chemin parcouru dans l'arbre.
III. 2 - Construction d'un arbre
La fonction suivante permet de séparer les données en deux groupes en utilisant l'indice de concentration de Gini. Nous allons détailler dans la suite les fonctions intervenant dans cette fonction. Pour rappel, la variable donnees est un tableau constitué de
def separe(donnees, p_var):
#Initialisation des parametres
b_ind,b_val,b_gini = inf, inf, inf
b_g, b_d = [],[]
m = len(donnees[0])-1
#extractions d'indices aléatoires
ind_var = indices_aleatoires(m,p_var)
for ind in ind_var:
for ligne in donnees:
#séparation des données en deux groupes
[gauche,droite]=test_separation(ind,ligne[ind],donnees)
gini = Gini_groupes([gauche,droite])
if gini < b_gini:
b_ind,b_val,b_gini = ind,ligne[ind],gini
b_g,b_d = gauche,droite
return [b_ind, b_val, b_g, b_d]
Q12. Écrire une fonction indices_aleatoires(m,p_var) qui prend en arguments le nombre m correspondant au nombre de colonnes disponibles et p_var un nombre permettant de tirer aléatoirement
On utilisera la fonction randrange (p) qui renvoie un entier aléatoirement entre 0 et
Pour obtenir l'indice de concentration de Gini total pour les deux groupes (gauche et droite), on réalise une somme des deux indices de concentration Gini
Q14. Compléter les instructions notées 1 à 5 de la fonction Gini_groupes donnée sur le document réponse qui prend en argument groupes la liste contenant les deux groupes à tester et qui renvoie l'indice de concentration de Gini.
- premier critère : quand le nombre de données à séparer est inférieur à une valeur que l'on notera taille_min,
- deuxième critère : quand le nombre de séparations a atteint une valeur maximale notée sep_max.
Q15. Écrire une fonction feuille(data) qui prend en argument un jeu de données data et qui renvoie la valeur de la classe majoritaire. La variable data est du même format que la variable donnees.
def construire_arbre(data_train, sep_max, taille_min, p_var):
arbre = separe(data_train, p_var)
construit(arbre, sep_max, taille_min, p_var, 1)
return arbre
- data_train des données dont on connaît déjà la classe;
- sep_max : le nombre de séparations maximales pouvant être effectuées avant de placer une feuille;
- taille_min : le nombre de données minimales sous lequel on impose de mettre une feuille plutôt que de séparer les données en deux;
- p_var : le nombre de valeurs à tirer aléatoirement pour le calcul de l'indice de concentration de Gini.
La construction de l'arbre est réalisée récursivement avec la fonction construit (arbre, sep_max, taille_min, p_var, ind_rec) après avoir créé un arbre initial à deux branches avec la fonction separe.
La fonction construit prend en argument entre autres : - arbre : structure de type noeud constituée de 4 éléments [ind, val, gauche, droite];
- sep_max;
- taille_min;
- p_var;
- ind_rec : l'indice de récursivité donnant le nombre de séparations déjà effectuées.
Dans la fonction récursive, la variable arbre de type nœud est modifiée au cours des appels successifs.
III. 3 - Test d'une prédiction sur un arbre simple
Pour tester les performances de prédiction dans ce cas, on utilise un jeu de 100 données connues pour construire l'arbre. On réalise ensuite une prédiction sur un jeu de 50 données (non utilisées pour la construction mais dont on connait la classe) : pour chaque donnée, on parcourt l'arbre jusqu'à arriver sur une feuille qui donnera le groupe prédit. On compare cette prédiction à la classe connue. On compte le nombre de succès pour en déduire un taux de réussite. On recommence cette analyse en faisant une nouvelle construction d'arbre à partir des 100 données (ce qui correspond aux prédictions notées 1,2 et 3
| Arbre construit à partir de 100 données | Arbre construit à partir de 125 données | Arbre construit à partir de 150 données | |
| Test de prédiction 1 | 79 % | 74 % | 88 % |
| Test de prédiction 2 | 76 % | 78 % | 84 % |
| Test de prédiction 3 | 74 % | 78 % | 77 % |
| Temps moyen |
|
|
0,86 s |
III. 4 - Algorithme des forêts aléatoires : «random forest»
Ensuite, on réalise une prédiction sur les différents arbres construits et on associe à la donnée à classer la classe majoritaire issue des différentes prédictions élémentaires.
On suppose que l'on dispose des fonctions : construire_foret qui renvoie une liste d'arbres (non détaillée ici) et prediction qui pour un arbre connu et une donnée renvoie la valeur de sa classe.
On note :
- data_train, les données d'entraînement de l'algorithme qui vont servir à construire les arbres;
- data_test, les données permettant de tester l'efficacité de l'algorithme en comparant le classement proposé par rapport à la valeur connue.
Conclusion
Le tableau 2 liste une synthèse des pourcentages de réussite obtenus ainsi que le temps pour réaliser une prédiction avec une forêt de 20 arbres.
|
|
|
|||||||
| Test de prédiction 1 |
|
|
|
||||||
| Test de prédiction 2 |
|
|
|
||||||
| Test de prédiction 3 |
|
|
|
||||||
| Temps moyen |
|
|
|
FIN

DOCUMENT RÉPONSE
|
|
![]() |
- | - | - | - | - | ![]() |
![]() |
- | - | ![]() |
![]() |
![]() |
- | ![]() |
| |
|
- |
|
![]() |
![]() |
![]() |
![]() |
|
|
![]() |
|
|
|||||||||||||||||||||||||||||||||||
![]() |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
![]() |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - (-) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
![]() |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

def Gini_groupes(groupes):
#nombre de données total
n_donnees = #instruction1
gini = 0.0 #somme pondérée des indices Gini de chaque groupe
for donnees in groupes:
taille = len (donnees) #taille d'un groupe
if taille != 0:
gini_gr = 0.0
for val in [0,1]:
p=0
for ligne in donnees :
if ligne[-1] == val:
#instruction2
#instruction3
gini_gr += #instruction4
#ajout de gini_gr avec le poids relatif
gini += #instruction5
return gini
NE RIEN ÉCRIRE DANS CE CADRE
Q15 - Fonction feuille(data)

def construit(arbre, sep_max, taille_min, p_var, ind_rec):
gauche, droite = arbre[2], arbre[3]
if : #condition1
valeur = feuille(gauche + droite)
arbre[2] = valeur
arbre[3] = valeur
return
if : #condition2
arbre[2], arbre[3] = feuille(gauche), feuille(droite)
if : #condition3
arbre[2] = feuille(gauche)
else:
arbre[2] = separe(gauche, p_var)
construit(arbre[2], sep_max, taille_min, p_var, ind_rec+1)
if : #condition4
arbre[3] = feuille(droite)
else:
arbre[3] = separe(droite, p_var)
construit(arbre[3], sep_max, taille_min, p_var, ind_rec+1)
def prediction(arbre, donnee):
[ind,val,gauche,droite]=arbre
if donnee[ind] < val:
if isinstance (gauche, list):
#instruction1
else:
#instruction2
else:
if isinstance(droite, list):
#instruction3
else:
#instruction4
.jpg)

ANNEXE
Rappels des syntaxes en Python
| Python | |||
| tableau à une dimension |
|
||
| accéder à un élément | v [0] renvoie 1 (L [0] également) | ||
| ajouter un élément | L. append(5) uniquement sur les listes | ||
| séquence équirépartie quelconque de 0 à 10.1 (exclus) par pas de 0.1 | arange(0,10.1,0.1) | ||
| définir une chaîne de caractères | mot='Python' | ||
| taille d'une chaîne | len(mot) | ||
| extraire des caractères | mot[2:7] | ||
| éliminer le
|
ligne.strip() | ||
| découper une chaîne de caractères selon un caractère passé en argument. On obtient une liste qui contient les caractères séparés | mot.split(',') | ||
| ouverture d'un fichier en lecture | with open ('nom_fichier', 'r') as file: instructions avec file |
Questions fréquentes
4 questionsSur quoi porte le sujet d'informatique CCINP PSI 2020 ?Afficher ou masquer la section
Questions fréquentes
4 questionsSur quoi porte le sujet d'informatique CCINP PSI 2020 ?
Il traite de la classification d'obstacles détectés par un sonar : traitement du signal avec numpy et spectrogramme, lecture de données, puis arbres de décision et forêts aléatoires programmés en Python.
Quelles erreurs le jury a-t-il relevées en informatique CCINP PSI 2020 ?
Décalages d'indices, variables non définies, confusion entre listes et tableaux numpy, mauvais arguments pour la transformée de Fourier (Q3) et méconnaissance de la taille d'un réel en double précision (Q9).
L'épreuve d'informatique CCINP PSI 2020 était-elle difficile ?
Selon le jury, elle a été bien réussie par la majorité des candidats. Les dernières questions (Q18 et Q19) ont cependant été rarement traitées.
Quelles questions travailler en priorité sur le sujet informatique CCINP PSI 2020 ?
Les questions jugées mal traitées par le jury : Q1, Q3, Q7, Q9, Q12 et Q16, ainsi que la fonction récursive de Q18.
Pas de description pour le moment

.jpg)
.jpg)
.jpg)
.jpg)
.jpg)
.jpg)
.jpg)
.jpg)

.jpg)
.jpg)
.jpg)
.jpg)
.jpg)
.jpg)