Clasificación multi label y regresión usando textos
En este blog explicare como entrenar un modelo svm para predecir a qué géneros pertenecen una película a partir de un texto que sirva como una descripción de la trama. También se entrena un modelo que usando esta misma descripción en texto prediga el año de salida de la película.
Libreta con el código implementado
Descripción de los datos
Los datos son sacados de kaggle y se pueden descargar de aqui: Wiki Movie Plots, la base de datos contiene descripciones para 34,886 películas de todo el mundo. las columnas son las siguientes:
- Release Year - Año en que se estreno la película
- Title - Titulo de la película
- Origin/Ethnicity - Origen de la pelicula (e.g. America, Bollywood, Tamil, etc)
- Director - Director(s)
- Cast - Actores y actrices principales
- Genre - Genero(s) de la película
- Wiki Page - URL de la pagina de Wikipedia de la cual la descripción de la trama fue tomada
- Plot - descripción larga de la trama de la película (ADVERTENCIA: puede contener espoilers)
Problemas específicos de este dataset que hay que solucionar
Los textos no están limpios: hay que remover todos caracteres raros como los puntos, comas, entre otros y reemplazar las contracciones que tienen los textos, ademas hay que realizar otras tareas de pln.
Los géneros no están en el formato correcto: en muchos casos nos encontramos con géneros que son iguales pero están mal escritos o escritos de otra forma, todos estos tenemos que cambiarlos por un único nombre, además tenemos que remover todos los géneros que sean demasiado raros y en algunos casos cambiarlos por otros más adecuados.
El problema de tener múltiples clases de salida
El problema de clasificar un dato tal que pueda pertenecer a múltiples clases tiene varias soluciones, una de ellas sería transformar el conjunto de clases en su conjunto potencia, donde cada subconjunto forme una nueva clase, el problema de esto es que el número de nuevas clases sería demasiado grande si antes tenias n clases ahora tendrás 2n nuevas clases, lo que dificulta el entrenamiento, para contrarrestar esto se podría utilizar solo únicamente aquellas clases que aparecen en el conjunto de entrenamiento, esto claro aumentaría nuestro sesgo, ya que si un dato pertenece a un subconjunto de clases que no está presente en el conjunto de entrenamiento no lo podremos clasificar correctamente.
Otra alternativa y la cual usaremos en este blog es entrenar un clasificador binario por clase, de manera que cada clasificador estime la probabilidad de un dato de pertenecer a cada clase independientemente del resto de clasificadores, la ventaja es que si tenemos n clases solo necesitamos n clasificadores para poder mapear un cualquier dato a todo el conjunto potencia de las clases, lo malo es que el conjunto vacío también está incluido, esto es que puede que todos los clasificadores tengan una salida falsa y por lo tanto existan datos que no pertenezcan a ninguna clase, esto es problema solo si sabemos de entrada que todos los datos tienen que pertenecer a mínimo una clase
La codificación de las entradas
Para poder meter nuestros textos a un algoritmo de aprendizaje es necesario buscar una representación matemática de estos, una forma seria bag of words, donde se convierte un texto a un vector en el cual cada posición representa una palabra, pudiendo haber un uno o un cero dependiendo de si esa palabra se encuentra en nuestro texto.
Un ejemplo sería considerando el siguiente vocabulario: {“hoy”, “día“, “yo”, “es”, “mañana”, “gran”, “vida”}
la representación vectorial de cada palabra es:
| Palabra | vector |
|---|---|
| hoy | 1,0,0,0,0,0,0,0 |
| día | 0,1,0,0,0,0,0,0 |
| yo | 0,0,1,0,0,0,0,0 |
| es | 0,0,0,1,0,0,0,0 |
| mañana | 0,0,0,0,1,0,0,0 |
| gran | 0,0,0,0,0,1,0,0 |
| vida | 0,0,0,0,0,0,1,0 |
| un | 0,0,0,0,0,0,0,1 |
Por lo que el texto “hoy es un gran día” es codificado de la siguiente manera:
1,1,0,1,0,1,0,1
La desventaja es que el vector es tan grande como nuestro vocabulario y que la mayoría de las entradas son ceros, otra manera de codificar el texto es usando tfidf en donde ya no solo se coloca un uno en caso de que la palabra esté presente sino que se cuenta el número de veces que la palabra apareció en el texto entre el número de documentos en los que aparece, de ahí el nombre Term Frequency Inverse Document Frequency. A pesar de que esta es una mejor manera de representar un texto aún tiene defectos, como que todavía tiene varias entradas en ceros o que no toma en cuenta el orden de las palabras, esto se puede resolver usando n gramas pero esto agrega más carga computacional.
Antes de codificar los textos hay que limpiarlos, primero, como nuestro texto tiene contracciones en el idioma inglés se reemplazan todas estas por la forma completa de la expresión y se eliminan todos los caracteres extraños como puntos, comas, signos de interrogación y de admiración entre otros. Luego se tokenizar el texto por palabras, estas se lematizan y se convierten a minúsculas.
Limpieza de los generos
Ahora hay que limpiar los géneros, para esto se removieron todos los géneros repetidos que estaban escritos de otra manera o que estaban mal escritos.
Luego se cambió el formato de múltiples géneros, cambiando todas la formas que existían para representar que una pelicula pertenece a multiples generos, por una en donde se pone cada género separado por el carácter “|”.
Hasta este momento ya tenemos los géneros limpios, pero si una película tiene múltiples géneros esto se sigue representando como una cadena, por lo que si queremos usar nuestros clasificadores binarios para cada clase lo que haremos será separar en una lista los géneros de cada película.
Ejemplo:
Si se tiene que una película es de los géneros “romance|comedy” esto se considera un género distinto del genero “romance” o solo “comedy”, para evitar que las combinaciones de distintas clases formen otra clase totalmente distinta, vamos a separar estas en una lista, convirtiendo asi “romance|comedy” a [“romance”, “comedy”].
Para representar esta lista se usará una representación de tipo one hot, pero con múltiples entradas, en donde la posición i-ésima del vector representa que la película pertenece al i-ésimo género, al final entrenaremos un clasificador binario para predecir si la i-ésima entrada es un uno o no.
Aprendizaje
Para el aprendizaje se utilizó una máquina de vectores de soporte lineal por género, ya que estas generalizan bien, se utilizaron solo los 23 géneros con más ocurrencia, eliminando los otros, y se removieron los datos que no pertenecían a ninguno de estos.
Como una muestra de los resultados se eligieron 25 películas aleatorias del conjunto de pruebas y se realizó la clasificación sobre ellas:
| Péliculas | Predicción | Real |
|---|---|---|
| Kitty Kornered | animation | comedy |
| Head in the Clouds | thriller | drama |
| Brown Sugar | comedy | |
| Delavine Affair | crime | comedy|romance |
| Red Salute | comedy | comedy |
| Take the Lead | drama | drama |
| Thalapathi | drama | |
| Vertigo | drama | comedy |
| Hochchheta ki | crime | |
| Guns of Darkness | horror | |
| Cat Napping | animation | drama |
| Girl with a Pearl Earring | drama | comedy |
| Ratatouille | drama | |
| Bareilly Ki Barfi | romance | drama |
| Fullmetal Alchemist the Movie: Conqueror of Sh… | fantasy | drama |
| Aakrosh | drama | |
| A World Apart | drama | comedy|crime |
| Child 44 | crime | musical |
| Steins;Gate: Fuka Ryōiki no Déjà vu | science_fiction | drama |
| The Gift of Love | drama | drama|war |
| Maria’s Lovers | adventure | |
| Beeba Boys | musical | |
| Alik Sukh | drama | drama |
| The Mysterious Mr. Valentine | drama|crime | drama |
| Verboten! | comedy |
Precisión de cada clasificador:
| Género | Precisión |
|---|---|
| drama | 0.719324 |
| comedy | 0.796415 |
| romance | 0.912416 |
| action | 0.923055 |
| thriller | 0.929612 |
| crime | 0.939959 |
| horror | 0.960799 |
| western | 0.984844 |
| musical | 0.966045 |
| science_fiction | 0.976537 |
| animation | 0.978723 |
| adventure | 0.971728 |
| family | 0.977703 |
| war | 0.981638 |
| fantasy | 0.982804 |
| mystery | 0.985281 |
| biography | 0.982221 |
| black | 0.986010 |
| history | 0.991110 |
| short | 0.991256 |
| martial_arts | 0.995337 |
| documentary | 0.994171 |
| sports | 0.995191 |
Presición promedio: 0.9528594781594922
Presición conjunta: 0.008
Incluso aun cuando tenemos una buena precisión por clase la precisión conjunta de todos los clasificadores no es buena, en nuestro caso estaría cercana a 0.08, esto debido a cómo funciona el método utilizado, la probabilidad de que clasifique bien una clase individualmente es alta, pero la probabilidad de que clasifique bien todas es el producto de las probabilidades individuales de cada clase, como todos son números menores a uno este producto termina siendo muy pequeño, por ejemplo.
En un caso hipotético supongamos que tenemos C clases distintas y que la probabilidad de clasificar buen una clase denotada como P es la misma para todas las clases, entonces la probabilidad de clasificar bien todas las clases es PC, aunque P sea un número muy grande como 0.95, si tenemos muchas clases PC terminará siendo un número muy pequeño, como es en nuestro caso.
Regresión para predecir los años de lanzamiento
Se realizó una regresión lineal usando las tramas vectorizadas de las películas para intentar predecir los años en que habían sido lanzadas, estos fueron algunos de los resultados:
| Pélicula | Predicción | Real |
|---|---|---|
| A Doll’s House | 1976 | 1973 |
| Adventure of the King | 2020 | 2010 |
| The Shoes of the Fisherman | 2031 | 1968 |
| There’s a Girl in My Soup | 1949 | 1970 |
| La Bohème | 1946 | 1926 |
| A Simple Plan | 1990 | 1998 |
| Chicken Run | 1989 | 2000 |
| Murder, Inc. | 1951 | 1960 |
| Home to Stay | 1947 | 1978 |
| The Number 23 | 1995 | 2007 |
| The Invisible Boy | 1968 | 1957 |
| A Boy, a Girl and a Bike | 1943 | 1949 |
| Disco Dancer | 2009 | 1982 |
| Rama Rama Krishna Krishna | 1991 | 2010 |
| Pyaar Ke Side Effects | 2020 | 2006 |
Conclusiones
A Pesar de las ventajas que ofrece la solución mostrada en el blog ya que se puede alcanzar un error muy bajo en la clasificación por clase, el error por clasificar un dato en las todas las clases juntas puede llegar a ser muy alto, ya que es el producto de los errores de todas las clases, por lo que si se tienen en particular muchas clases quiza sea mejor intentar usar otro metodo.
En la regresión para encontrar los años de las películas encontramos que puede que no exista una relación entre la trama y el año, o tal vez se puedan alcanzar mejores resultados usando una red neuronal.
Para ambos casos quizá usar redes neuronales o otro método de codificación pueda mejorar la precisión, world 2 vec permite codificar las palabras a vectores densos de una dimensión especificada, en donde la cual se trata de modelar la cercanía de las palabras, o tal vez usar el universal sentence encoder, el cual toma un texto y lo convierte a un vector denso de 512, esto está pensado para obtener la similitud de textos en base a la distancia coseno. Además de estos también existen otros métodos que se podrían utilizar.