Ce projet est basé sur une compétition Kaggle organisée par : Microsoft, Windows Defender ATP Research, Northeastern University College of Computer and Information Science, et Georgia Tech Institute for Information Security & Privacy.
L'industrie des logiciels malveillants continue d'être un marché bien organisé et très rentable qui se consacre à échapper aux mesures de sécurité traditionnelles. Une fois qu'un ordinateur est infecté par un logiciel malveillant, les criminels peuvent nuire aux consommateurs et aux entreprises de nombreuses façons (notamment par le chantage comme c’était le cas en mai 2017 avec le ransomware WannaCrypt).
Le but de ce projet est de prédire la probabilité qu'une machine Windows soit infectée par un logiciel malveillant, en fonction des différentes propriétés de cette machine. De ce fait, il sera plus aisé de déterminer les failles et de les corriger soit par des patchs soit pour les nouvelles machines.
Ce projet a été réalisé dans le cadre de mon Master 2 Mathématiques Appliquées, Data science à l'Université d'Aix-Marseille.
- • Traitement de données massives
- • Optimisation
- • Statistique exploratoire
- • Analyse prédictive
- • Visualisation de données
- • Machine Learning
-
• Données à télécharger dans le repo Kaggle : https://www.kaggle.com/c/microsoft-malware-prediction/data
-
• Python 3.6
- → numpy
- → pandas
- → matplotlib
- → seaborn
-
Un fichier train.csv (d’une taille de 4.2 Go)
-
Un fichier test.csv (d’une taille de 3.8 Go)
Le fichier train.csv possède 8,9 millions de lignes (chacune correspond à une machine unique identifiée par la variable MachineIdentifier) et 83 colonnes labellisées. La dernière colonne de cette table de données correspond à la variable HasDetections qui informe si la machine a été infectée (HasDetections = 1) ou non (HasDetections = 0).
Le fichier test.csv possède 7,8 millions de lignes et 82 colonnes. La 83ème colonne sera une variable HasDetections à créer et correspondra à la probabilité que la machine soit infectée. Cette variable sera un nombre réel compris entre 0,0 et 1,0.
Le nom est la signification des variables est disponible dans le fichier dictionnaire.pdf
Le plus grand défi de cette compétition est donc de traiter ces tables de données de grande dimension. En effet, le simple fait d’importer la table de données d’entrainement via la méthode classique read_csv fait planter le processus et parfois même le système dans son entièreté à cause d’un dépassement de mémoire vive.
La solution a été de reformater les variables. En effet, Python formate automatiquement les variables en : object, float64 et int64 qui sont tous les trois codés sur 8 octets. Pourtant certaines variables ne possèdent que des valeurs binaires, on demande donc une place de 8 octets pour la remplir avec une valeur de 1 octet.
C’est pourquoi les variables contenant seulement des valeurs binaires ont été reformatées en int8, celles contenant des valeurs binaires mais aussi des NaN ont été reformatées en float16. Toutes les variables numériques encodées en int64 et float64 ont été reformatées respectivement en int32 et float32.
De cette façon, au chargement des données on passe de 37 Go d'utilisation de la mémoire à 1,6 Go. Le gain est énorme, il se compte en facteur 24.
Nous commençons par regarder le type des variables de la table de données :
train.info();
dtypes: category(30), float16(27), float32(9), int16(5), int32(1), int8(11)
Nous remarquons que les données sont composées de 30 variables catégorielles et 53 variables numériques. Les variables qui risquent de poser problèmes sont les variables catégorielles car elles ne peuvent être comprises par un algorithme de manière brute.
→ Distribution de la part des machines infectées et non-infectées par un virus (variable à prédire)
La moitié des machines de la table de données est infectée par un virus. La variable cible est bien équilibrée. Cela devrait faciliter la prédiction sur les données test (qui sont elles aussi équilibrées de la même manière d’après l’organisateur de la compétition)
→ Le nombre de variables étant élevé, nous allons supprimer les moins pertinentes. Pour cela nous cherchons les variables dont le nombre de données manquantes est élevés.
Nous retirons alors les variables dont plus de 50 % des données sont manquantes : PuaMode, Census_ProcessorClass, DefaultBrowsersIdentifier, Census_IsFlightingInternal, Census_InternalBatteryType, Census_ThresholdOptIn, Census_IsWIMBootEnabled. Il nous reste alors 76 variables.
→ Ensuite nous cherchons les données trop asymétriques. En effet, pour certaines variables, une seule modalité couvre plus de 99% des occurrences, il est donc inutile de la garder car elle ne peut pas discriminer les données de façon pertinente. En effet, prenons pour exemple la variable « IsBeta », elle code 0 si le logiciel Windows Defender n’est pas en version Beta et 1 si ce logiciel est en version Beta. Voici les résultats :
Comme la version par défaut est la version finie (donc non beta) sur un PC Windows, la quasi-totalité des machines sont sur celle-ci. Seulement 67 machines sur 9 millions possèdent la version beta de Windows Defender. On parle alors de coefficient d’asymétrie et il est ici égal à 99,9992. Cette variable ne nous intéresse donc pas car elle est inutile et risque même de fausser le modèle de prédiction. Ceci était un exemple, on va chercher toutes les variables asymétriques et les supprimer de notre table de données d’entrainement.
Nous retiendrons tout d’abord le seuil de 99%, c’est-à-dire que nous enlèverons toutes les variables dont le coefficient d’asymétrie est supérieur à 99,00. Ce seuil est assez arbitraire, nous retiendrons cependant les autres variables et leur coefficient au cas où nous souhaiterons en supprimer d’autres afin d’affiner le modèle.
Cela nous amène donc à supprimer 8 variables, il nous reste alors 68 variables.
→ Le nombre de variables étant encore élevé, nous allons supprimer les variables qui sont très corrélées entre elles. En effet, deux variables extrêmement corrélées signifieraient que l’une d’elle est inutile car elle serait redondante. Pour cette étape, seules les variables numériques seront considérées. Ces variables sont de type float16 (20), float32 (8), int8(8), int16 (5) et int32(1). Nous avons donc au final comme output un tableau des corrélations de dimension 42x42.
Les valeurs entourées sont les paires de corrélation les plus élevées (supérieures à 0.80 ou inférieures -0.80)
Résumé des variables très corrélées :
A information égale, on préfère avoir à gérer le moins de modalités possible, c’est pourquoi les variables que nous avons gardées sont : IsSxsPassiveMode, Census_ProcessorManufacturerIdentifier, OsBuild et Census_OSInstallLanguageIdentifier. Il reste alors 64 variables.
→ Par la suite, je regarde les variables les plus corrélées à la variable cible (HasDetections). En effet, ma logique est que si une variable n’influe pas sur le fait d’avoir un virus alors elle est inutile et on pourra la supprimer des données d’entrainement.
Les valeurs en gras représentent un coefficient de corrélation avec HasDetections inférieur à 2%.
A la suite de cette exploration, 19 variables numériques ont pu être supprimées car elles n’influaient pas vraiment sur notre variable cible HasDetections. Il reste alors 45 variables dans notre jeu de données d’entrainement.
Les variables les plus corrélées (positivement ou négativement) à la cible HasDetections sont :
- AVProductsInstalled : - 0,1496
- AVProductStatesIdentifier : 0,1174
- Census_IsAlwaysOnAlwaysConnectedCapable : - 0,0628
- IsProtected : 0,057
- Census_TotalPhysicalRAM : 0,0571
- Census_ProcessorCoreCount : 0,0543
- Wdft_IsGamer : 0,0539
Les variables les plus corrélées sont liées :
- A la configuration de l'antivirus dans la machine, ce qui semble logique
- Aux configurations matérielles de la machine
A la suite de ces étapes de manipulation de données, nous avons choisi de finalement réduire le jeu de données en ne considérant que les variables les plus corrélées à la variable cible HasDetections accompagné de cette dernière variable.
Nous aurons donc comme jeu de données train que nous utiliserons dans le fichier train_corr.py, une sous-matrice de dimension 8 921 483 * 8 et nous ne possèdons plus que des variables numériques.
La plupart des utilisateurs n'ont qu'un seul antivirus installé sur leur ordinateur, mais un nombre considérable d'entre eux en ont également deux. Un petit nombre d'utilisateurs semblent très inquiets d'attraper un virus et ont trois logiciels différents pour protéger leurs systèmes. Il y a aussi des utilisateurs encore plus inquiets qui utilisent 4 ou même 5 logiciels antivirus différents. Ces utilisateurs qui possèdent plus d’un antivirus ont peut-être raison car cette variable est inversement corrélée à celle de l’infection par un malware avec un coefficient de corrélation de - 0.14. Plus une machine possède d’antivirus et moins elle a de chance d’attraper un virus.
Census_IsAlwaysOnAlwaysConnectedCapable : Indique si la batterie permet à l'appareil d'être en mode « toujours connecté »
Le mode « toujours connecté » est un nouveau système de Microsoft. Il n’existe donc que sur des PC neufs et chers (aux alentours de 1200€). Il est donc logique que peu de machine en terme de pourcentage (6 %) possèdent cette fonctionnalité. On remarque que les machines possédant cette fonctionnalité ont moins de chance d’être infecté par un virus.
Cette variable retourne :
- a. VRAI (1) s'il y a au moins un produit antivirus actif et à jour qui fonctionne sur la machine.
- b. FAUX (0) s'il n'y a pas d’antivirus actif sur cette machine, ou si l'antivirus est actif mais n’est pas mis à jour.
Plus un PC possède une capacité mémoire vive importante et plus il a de chance d’être infecté avec un virus. Cela est en cohérence avec les distributions qui suivent et qui montrent que plus une machine est de meilleure qualité en terme de performance (mémoire RAM élevée, nombre de processeur élevé, PC gamer) et plus il s’infecte en malware.
La majorité des PC ont 2, 4 ou 8 coeurs logiques cependant certains en possèdent 1 ou 6. Plus un PC possède de coeurs logiques et plus il a de chance d’être infecté avec un virus
Wdft_IsGamer : Indique si l'appareil est un PC Gamer ou non basé sur ses configurations et composants matériels
Les PC de configuration « gamer » représentent presque 1/3 du nombre total des machines. C'est une proportion assez impressionnante. Cela ne nous indique cependant pas si l’utilisateur est un joueur de jeux vidéo mais seulement de la qualité de sa configuration.
Les variables AVProductsInstalled et AVProductStatesIdentifier sont très corrélés entre elles (coefficient de corrélation = - 0,63). De plus nous avions vu précédemment que la variables AVProductStatesIdentifier contient une quantité importante de valeurs unique (étant donné que c’est un identifiant). Pour ces deux raisons, nous choisirons d’enlever cette variable du jeu de donnée d’entrainement. Pour les mêmes raisons, nous enlèverons la variables Census_TotalPhysicalRAM (corrélation avec Census_ProcessorCoreCount de 0,60 et nombre de valeurs uniques de 3446).
Le choix qui a été fait est d’apprendre sur les 8,9 millions d’observations et 6 covariables avec un modèle de régression linéaire. Nous avons choisi cela car les variables sélectionnées s’y prêtaient bien (elles sont corrélées linéairement à la variable cible) et aussi car ce modèle peut nous renvoyer un résultat continu. En effet, bien que la variable cible dans les données d’entraînement est binaire, ce n’est pas le cas pour les données test où l’on attend une valeur de probabilité allant de 0 à 1.
Après avoir appris le modèle de régression sur les données d’entraînement, on l’applique sur les données test. Cela renvoie un tableau de données avec 7 millions d’observations, et deux colonnes (l’identifiant et la cible).
Le résultat est soumis sur le site Kaggle puis le score est calculé en utilisant un résidu des erreurs observés (appelé courbe ROC).
Le score pour ce jeu de données est de 0.60136.