Clasificación de spam con ML-Pack

[*]Introducción

Paquete ML es una biblioteca de aprendizaje automático C++ de tamaño reducido que se puede integrar fácilmente en otros programas. Es un proyecto de código abierto desarrollado activamente y lanzado bajo un Licencia BSD-3 . El aprendizaje automático ha ganado popularidad debido a la gran cantidad de datos electrónicos que se pueden recopilar. Algunos otros marcos populares de aprendizaje automático incluyen TensorFlow , MXNet , PyTorch , Encadenador y Pádel Pádel , sin embargo, estos están diseñados para flujos de trabajo más complejos que ML-Pack. En FedoraML-Pack está empaquetado por su desarrollador principal ryan curtin . Además de una interfaz de línea de comandos, ML-Pack tiene enlaces para Pitón y julia . Aquí, nos centraremos en la interfaz de la línea de comandos, ya que puede ser útil para que los administradores del sistema la integren en sus flujos de trabajo.

Instalación

Puede instalar ML-Pack en el Fedora línea de comando usando

                      $ sudo dnf -y install mlpack mlpack-bin
                    

También puede instalar la documentación, los encabezados de desarrollo y los enlaces de Python usando…

                      $ sudo dnf -y install  mlpack-doc 
mlpack-devel mlpack-python3
                    

aunque no se utilizarán en esta introducción.

Ejemplo

como un example, entrenaremos un modelo de aprendizaje automático para clasificar los mensajes SMS de spam. Para que este artículo sea breve, los comandos de Linux no se explicarán completamente, pero puede obtener más información sobre ellos utilizando el comando man, por ejemplo. example para el primer comando usado a continuación, wget

                      $ man wget
                    

le dará información de que wget descargará archivos de la web y las opciones que puede usar para ello.

Obtener un conjunto de datos

Usaremos un example conjunto de datos de spam en indonesio proporcionado por Yudi Wibisono

                      

$ wget https://drive.google.com/file/d/1-stKadfTgJLtYsHWqXhGO3nTjKVFxm_Q/view
                      
$ unzip dataset_sms_spam_bhs_indonesia_v1.zip

Conjunto de datos de preprocesamiento

Intentaremos clasificar un mensaje como spam o ham por el número de ocurrencias de una palabra en un mensaje. Primero cambiamos los finales de línea del archivo, eliminamos la línea 243 a la que le falta una etiqueta y luego eliminamos el encabezado del conjunto de datos. Luego, dividimos nuestros datos en dos archivos, etiquetas y mensajes. Dado que las etiquetas están al final del mensaje, el mensaje se invierte y luego se quita la etiqueta y se coloca en un archivo. Luego, el mensaje se elimina y se coloca en otro archivo.

                      $ tr 'r' 'n' < dataset_sms_spam_v1.csv > dataset.txt
$ sed '243d' dataset.txt > dataset1.csv
$ sed '1d' dataset1.csv > dataset.csv
$ rev dataset.csv | cut -c1  | rev > labels.txt
$ rev dataset.csv | cut -c2- | rev > messages.txt
$ rm dataset.csv
$ rm dataset1.csv
$ rm dataset.txt
                    

El aprendizaje automático funciona con datos numéricos, por lo que usaremos etiquetas de 1 para jamón y 0 para spam. El conjunto de datos contiene tres etiquetas, 0, sms normal (jamón), 1, fraude (spam) y 2 promoción (spam). Etiquetaremos todo el spam como 1, por lo que las promociones y el fraude se etiquetarán como 1.

                      $ tr '2' '1' < labels.txt > labels.csv
$ rm labels.txt
                    

El siguiente paso es convertir todo el texto de los mensajes a minúsculas y, para simplificar, eliminar la puntuación y cualquier símbolo que no sea espacios, finales de línea o que estén en el rango az (se necesitaría expandir este rango de símbolos para uso en producción)

                      $ tr '[:upper:]' '[:lower:]' < 
messages.txt > messagesLower.txt
$ tr -Cd 'abcdefghijklmnopqrstuvwxyz n' < 
 messagesLower.txt > messagesLetters.txt
$ rm messagesLower.txt
                    

Ahora obtenemos una lista ordenada de palabras únicas utilizadas (este paso puede demorar unos minutos, así que use agradable para darle una prioridad baja mientras continúa con otras tareas en su computadora).

                      $ nice -20 xargs -n1 < messagesLetters.txt > temp.txt
$ sort temp.txt > temp2.txt
$ uniq temp2.txt > words.txt
$ rm temp.txt
$ rm temp2.txt
                    

Luego creamos una matriz, donde para cada mensaje, se cuenta la frecuencia de ocurrencia de palabras (más sobre esto en Wikipedia, aquí y aquí ). Esto requiere unas pocas líneas de código, por lo que el script completo, que debe guardarse como ‘makematrix.sh’, está debajo

                      #!/bin/bash
declare -a words=()
declare -a letterstartind=()
declare -a letterstart=()
letter=" "
i=0
lettercount=0
while IFS= read -r line; do
 labels[$((i))]=$line
 let "i++"
done < labels.csv
i=0
while IFS= read -r line; do
 words[$((i))]=$line
 firstletter="$( echo $line | head -c 1 )"
 if [ "$firstletter" != "$letter" ]
 then
  letterstartind[$((lettercount))]=$((i))
  letterstart[$((lettercount))]=$firstletter
  letter=$firstletter
  let "lettercount++"
 fi
 let "i++"
done < words.txt
letterstartind[$((lettercount))]=$((i))
echo "Created list of letters"

touch wordfrequency.txt
rm wordfrequency.txt
touch wordfrequency.txt
messagecount=0
messagenum=0
messages="$( wc -l messages.txt )"
i=0
while IFS= read -r line; do
 let "messagenum++"
 declare -a wordcount=()
 declare -a wordarray=()
 read -r -a wordarray <<< "$line"
 let "messagecount++"
 words=${#wordarray[@]}
 for word in "${wordarray[@]}"; do
  startletter="$( echo $word | head -c 1 )"
  j=-1
  while [ $((j)) -lt $((lettercount)) ]; do
   let "j++"
   if [ "$startletter" == "${letterstart[$((j))]}" ]
   then
    mystart=$((j))
   fi
  done
  myend=$((mystart))+1
  j=${letterstartind[$((mystart))]}
  jend=${letterstartind[$((myend))]}
  while [ $((j)) -le $((jend)) ]; do
   wordcount[$((j))]=0
   if [ "$word" == "${words[$((j))]}" ]
   then
    wordcount[$((j))]="$( echo $line | grep -o $word | wc -l )"
   fi
   let "j++"
  done
 done
 for j in "${!wordcount[@]}"; do
  wordcount[$((j))]=$(echo " scale=4; 
  $((${wordcount[$((j))]})) / $((words))" | bc)
 done
 wordcount[$((words))+1]=$((words))
 echo "${wordcount[*]}" >> wordfrequency.txt
 echo "Processed message ""$messagenum"
 let "i++"
done < messagesLetters.txt
# Create csv file
tr ' '  ',' < wordfrequency.txt > data.csv

                    

Ya que Intento es un lenguaje interpretado, esta implementación simple puede tardar hasta 30 minutos en completarse. Si usa el script Bash anterior en su estación de trabajo principal, ejecútelo como una tarea con baja prioridad para que pueda continuar con otro trabajo mientras espera:

                      $ nice -20 bash makematrix.sh
                    

Una vez que el script haya terminado de ejecutarse, divida los datos en conjuntos de prueba (30 %) y de entrenamiento (70 %):

                      $ mlpack_preprocess_split                      
    --input_file data.csv                     
    --input_labels_file labels.csv            
    --training_file train.data.csv            
    --training_labels_file train.labels.csv   
    --test_file test.data.csv                 
    --test_labels_file test.labels.csv        
    --test_ratio 0.3                          
    --verbose
                    

entrenar a un modelo

Ahora entrena un Modelo de regresión logística :

                      $ mlpack_logistic_regression 
--training_file train.data.csv 
--labels_file train.labels.csv --lambda 0.1 
--output_model_file lr_model.bin
                    

Probar el modelo

Finalmente probamos nuestro modelo produciendo predicciones,

                      $ mlpack_logistic_regression 
--input_model_file lr_model.bin 
 --test_file test.data.csv 
--output_file lr_predictions.csv
                    

y comparando las predicciones con los resultados exactos,

                      $ export incorrect=$(diff -U 0 lr_predictions.csv 
test.labels.csv | grep '^@@' | wc -l)
$ export tests=$(wc -l < lr_predictions.csv)
$ echo "scale=2;  100 * ( 1 - $((incorrect)) 
/ $((tests)))"  | bc
                    

Esto da una tasa de validación de aproximadamente el 90%, similar a la obtenida aquí .

El conjunto de datos se compone de aproximadamente un 50 % de mensajes de spam, por lo que las tasas de validación son bastante buenas sin necesidad de ajustar demasiado los parámetros. En casos típicos, los conjuntos de datos están desequilibrados con muchas más entradas en algunas categorías que en otras. En estos casos, se puede obtener una buena tasa de validación prediciendo erróneamente la clase con unas pocas entradas. Por lo tanto, para evaluar mejor estos modelos, se puede comparar el número de clasificaciones erróneas de spam y el número de clasificaciones erróneas de jamón. De particular importancia en las aplicaciones es la cantidad de resultados de spam falsos positivos, ya que normalmente no se transmiten. La siguiente secuencia de comandos produce una matriz de confusión que da una mejor indicación de clasificación errónea. Guárdelo como ‘confusion.sh’

                      #!/bin/bash
declare -a labels
declare -a lr
i=0
while IFS= read -r line; do
        labels[i]=$line
        let "i++"
done < test.labels.csv
i=0
while IFS= read -r line; do
        lr[i]=$line
        let "i++"
done < lr_predictions.csv
TruePositiveLR=0
FalsePositiveLR=0
TrueZeroLR=0
FalseZeroLR=0
Positive=0
Zero=0
for i in "${!labels[@]}"; do
        if [ "${labels[$i]}" == "1" ]
        then
                let "Positive++"
                if [ "${lr[$i]}" == "1" ] 
                then
                        let "TruePositiveLR++"
                else
                        let "FalseZeroLR++"
                fi
        fi
        if [ "${labels[$i]}" == "0" ]
        then
                let "Zero++"
                if [ "${lr[$i]}" == "0" ]
                then
                        let "TrueZeroLR++"
                else
                        let "FalsePositiveLR++"
                fi
        fi

done
echo "Logistic Regression"
echo "Total spam"  $Positive
echo "Total ham"  $Zero
echo "Confusion matrix"
echo "             Predicted class"
echo "                 Ham | Spam "
echo "              ---------------"
echo " Actual| Ham  | " $TrueZeroLR "|" $FalseZeroLR
echo " class | Spam | " $FalsePositiveLR " |" $TruePositiveLR
echo ""

                    

luego ejecute el script

                      $ bash confusion.sh
                    

Debería obtener una salida similar a

Regresión logística
Correo no deseado total 183
Total jamón 159
Matriz de confusión

Clase predicha
jamón Correo no deseado
clase real jamón 128 26
Correo no deseado 31 157

lo que indica un nivel razonable de clasificación. Otros métodos que puede probar en ML-Pack para este problema incluyen bayesiana ingenua , bosque aleatorio , árbol de decisión , AdaBoost y perceptrón .

Para mejorar la clasificación de errores, puede probar otros métodos de preprocesamiento en el conjunto de datos inicial. Las redes neuronales pueden proporcionar tasas de validación de hasta el 99,95 %; consulte example aquí , aquí y aquí . Sin embargo, el uso de estas técnicas con ML-Pack no se puede hacer en la interfaz de la línea de comandos en la actualidad y se trata mejor en otra publicación.

Para obtener más información sobre ML-Pack, consulte el documentación .

Related Posts