
Hace un mes, Lenta lanzó un concurso en el que el mismo Talking Hat de Harry Potter identifica a los participantes que tienen acceso a la red social a una de las cuatro facultades. La competencia se realizó bien, los nombres que suenan de manera diferente están determinados por diferentes facultades, y los nombres y apellidos similares en inglés y ruso se distribuyen de manera similar. No sé si la distribución depende solo de los nombres y apellidos, y si el número de amigos u otros factores se tiene en cuenta de alguna manera, pero esta competencia sugirió la idea de este artículo: tratar de entrenar a un clasificador desde cero, lo que permitirá que los usuarios se distribuyan a diferentes facultades.
En este artículo, haremos un modelo ML simple que distribuya a las personas a los departamentos de Harry Potter dependiendo de su nombre y apellido, después de haber pasado por un pequeño proceso de investigación siguiendo la metodología CRISP . A saber nosotros:
- Formulamos el problema;
- Investigamos posibles enfoques para su solución y formulamos requisitos de datos (Métodos de solución y datos);
- Recopilaremos los datos necesarios (métodos de solución y datos);
- Estudiaremos el conjunto de datos recopilados (Investigación exploratoria);
- Extraer características de datos sin procesar (Feature Engineering);
- Enseñemos un modelo de aprendizaje automático (evaluación del modelo);
- Compare los resultados obtenidos, evalúe la calidad de las soluciones y, si es necesario, repita los párrafos 2-6;
- Empacamos la solución en un servicio que se puede usar (Producción).

Esta tarea puede parecer trivial, por lo que impondremos una restricción adicional en todo el proceso (para que tome menos de 2 horas) y en este artículo (para que su tiempo de lectura sea inferior a 15 minutos).
Si ya está inmerso en el hermoso y maravilloso mundo de la Ciencia de Datos y no ve a Kagglite constantemente, o (Dios no quiera) medir la duración de su Hadup durante las reuniones con colegas, lo más probable es que el artículo le parezca simple y sin interés. Además: la calidad de los modelos finales no es el valor principal de este artículo. Te lo hemos advertido. Vamos
Un repositorio de github con el código utilizado en el artículo también está disponible para lectores curiosos. En caso de errores, abra PR.
Es posible resolver un problema que no tiene criterios de decisión claros durante un tiempo infinitamente largo, por lo que decidiremos de inmediato que queremos obtener una solución que nos permita obtener la respuesta "Gryffindor", "Ravenclaw", "Hufflepuff" o "Slytherin" en respuesta a la línea ingresada.
De hecho, queremos obtener una caja negra:
" " => [?] => Griffindor
El sombrero negro original distribuía jóvenes magos a los departamentos dependiendo de su naturaleza y cualidades personales. Dado que los datos sobre el carácter y la personalidad de acuerdo con las condiciones de la tarea no están disponibles para nosotros, utilizaremos el nombre y el apellido del participante, recordando que en este caso debemos distribuir los caracteres del libro entre los departamentos que corresponden a sus departamentos nativos del libro. Y los potteromanos definitivamente estarían molestos si nuestra decisión distribuyera a Harry a Hufflepuff o Ravenclaw (pero debería enviar a Harry a Gryffindor y Slytherin con la misma probabilidad de transmitir el espíritu del libro).
Como estamos hablando de probabilidades, formalizamos el problema en términos matemáticos más rigurosos. Desde el punto de vista de Data Science, resolvemos el problema de clasificación, es decir, asignar a un objeto (línea, en forma de nombre y apellido) una determinada clase (de hecho, es solo una etiqueta o etiqueta, que puede ser un número o 4 variables que tienen un valor sí / no ) Entendemos que al menos en el caso de Harry será correcto dar 2 respuestas: Gryffindor y Slytherin, por lo que sería mejor no predecir la facultad específica que define el sombrero, sino la probabilidad de que una persona sea asignada a esta facultad, por lo que nuestra decisión se tomará en tipo de alguna función
Evaluación de métricas y calidad
La tarea y el objetivo están formulados, Ahora pensaremos cómo resolverlo pero eso no es todo. Para comenzar el estudio, debe ingresar métricas de calidad. En otras palabras, para determinar cómo compararemos 2 soluciones diferentes entre sí.
Todo en la vida es bueno y simple: entendemos intuitivamente que un detector de correo no deseado debe pasar un mínimo de correo no deseado a los mensajes entrantes, así como pasar un máximo de las letras necesarias y ciertamente no debe enviar las cartas necesarias al correo no deseado.
En realidad, todo es más complicado y la confirmación de esto es una gran cantidad de artículos que explican cómo y qué métricas se utilizan. La práctica ayuda a entender esto mejor, pero este es un tema tan voluminoso que prometemos escribir una publicación separada sobre él y hacer una mesa abierta para que todos puedan jugar y comprender en la práctica cómo difiere esto.
El hogar "pero escojamos lo mejor" para nosotros será ROC AUC . Esto es exactamente lo que queremos de la métrica en este caso: cuanto menos falsos positivos y más precisa la predicción real, mayor es el AUC de ROC.
Para un modelo ROC ideal, AUC es 1, para un modelo aleatorio ideal que define clases absolutamente al azar - 0.5.
Algoritmos
Nuestra caja negra debe tener en cuenta la distribución de los héroes de los libros, tomar un nombre y apellido diferentes como entrada y dar el resultado. Para resolver el problema de clasificación, puede usar diferentes algoritmos de aprendizaje automático:
redes neuronales, máquinas de factorización, regresión lineal o, por ejemplo, SVM.
Contrariamente a la creencia popular, Data Science no se limita solo a redes neuronales, y para popularizar esta idea, en este artículo, las redes neuronales se dejan como un ejercicio para un lector curioso . Aquellos que no tomaron un solo curso en análisis de datos (especialmente el subjetivamente mejor del SDO), o simplemente leyeron n noticias sobre aprendizaje automático o IA, que ahora se publican incluso en las revistas de pescadores aficionados, deben haber conocido los nombres de grupos generales de algoritmos : embolsado, refuerzo, método de vector de soporte (SVM), regresión lineal. Son ellos los que usaremos para resolver nuestro problema.
Y para ser más precisos, comparamos:
- Regresión lineal
- Impulso (XGboost, LightGBM)
- Decidir árboles (estrictamente hablando, este es el mismo impulso, pero lo sacaremos por separado: Árboles adicionales)
- Ensacado (bosque aleatorio)
- SVM
Podemos resolver el problema de distribuir a cada estudiante de Hogwarts a una de las facultades definiendo la facultad que le corresponde, pero estrictamente hablando, esta tarea se reduce a resolver el problema de determinar si cada clase pertenece individualmente. Por lo tanto, en el marco de este artículo nos fijamos el objetivo de obtener 4 modelos, uno para cada facultad.
Datos
Encontrar el conjunto de datos adecuado para la capacitación y, lo que es más importante, legal para usarlo con el propósito correcto, es una de las tareas más complejas y que requieren mucho tiempo en Data Science. Para nuestra tarea, tomaremos los datos de wikia en todo el mundo de Harry Potter. Por ejemplo, en este enlace puedes encontrar todos los personajes que estudiaron en la facultad de Gryffindor. Es importante que en este caso utilicemos los datos para fines no comerciales, por lo tanto, no violamos la licencia de este sitio.

Para aquellos que piensan que los Data Scientists son muy buenos, iré a Data Scientists y me dejaré enseñarles, les recordamos que hay un paso como limpiar y preparar datos. Los datos descargados se deben moderar manualmente para eliminar, por ejemplo, "El Séptimo Prefecto de Gryffindor" y eliminar la "Chica desconocida de Gryffindor" de forma semiautomática. En el trabajo real, una parte proporcionalmente grande de la tarea siempre está asociada con la preparación, limpieza y restauración de valores perdidos en el conjunto de datos.
Un poco de ctrl + c & ctrl + v y en la salida obtenemos 4 archivos de texto, que contienen los nombres de los caracteres en 2 idiomas: inglés y ruso.
Estudiamos los datos recopilados (EDA, Análisis Exploratorio de Datos)
Para esta etapa, tenemos 4 archivos que contienen los nombres de estudiantes de facultades, veremos con más detalle:
$ ls ../input griffindor.txt hufflpuff.txt ravenclaw.txt slitherin.txt
Cada archivo contiene 1 nombre y apellido (si corresponde) del alumno por línea:
$ wc -l ../input/*.txt 250 ../input/griffindor.txt 167 ../input/hufflpuff.txt 180 ../input/ravenclaw.txt 254 ../input/slitherin.txt 851 total
Los datos recopilados son de la forma:
$ cat ../input/griffindor.txt | head -3 && cat ../input/griffindor.txt | tail -3 Charlie Stainforth Melanie Stanmore Stewart
Toda nuestra idea se basa en el supuesto de que hay algo similar en los nombres y apellidos que nuestra caja negra (o sombrero negro) puede aprender a distinguir.
El algoritmo puede alimentar las líneas tal cual, pero el resultado no será bueno, porque los modelos básicos no podrán comprender de forma independiente cómo "Draco" difiere de "Harry", por lo que tendremos que extraer signos de nuestros nombres y apellidos.
Preparación de datos (ingeniería de características)
Los signos (o características, de la característica característica en inglés ) son las propiedades distintivas de un objeto. La cantidad de veces que una persona cambió de trabajo durante el año pasado, la cantidad de dedos en su mano izquierda, la capacidad del motor, si el millaje del automóvil excede los 100,000 km o no. Todo tipo de clasificaciones de signos fueron inventadas por un gran número, no hay ni puede haber un solo sistema a este respecto, por lo que daremos ejemplos de qué signos pueden ser:
- Número racional
- Categoría (hasta 12, 12-18 o 18+)
- Valor binario (devolvió el primer préstamo o no)
- Fecha, color, acciones, etc.
La búsqueda (o formación) de características (en inglés Feature Engineering ) a menudo se destaca como una etapa separada de investigación o el trabajo de un especialista en análisis de datos. De hecho, el sentido común, la experiencia y las pruebas de hipótesis ayudan en el proceso mismo. Adivinar los signos correctos de inmediato es una combinación de mano completa, conocimiento fundamental y suerte. A veces hay chamanismo, pero el enfoque general es muy simple: debe hacer lo que le viene a la mente y luego verificar si fue posible mejorar la solución agregando un nuevo atributo. Por ejemplo, como una señal para nuestra tarea, podemos tomar el número de chisporroteos en el nombre.
En la primera versión (porque el estudio real de Data Science, como obra maestra, nunca se puede completar) de nuestro modelo, utilizaremos las siguientes características para el nombre y el apellido:
- 1 y la última letra de la palabra - vocal o consonante
- Vocales dobles y consonantes
- Número de vocales, consonantes, sordas, sonoras
- Longitud del nombre, longitud del apellido
- ...
Para hacer esto, tomaremos este repositorio como base y agregaremos una clase para que pueda usarse para letras latinas. Esto nos dará la oportunidad de determinar cómo suena cada letra.
>> from Phonetic import RussianLetter, EnglishLetter >> RussianLetter('').classify() {'consonant': True, 'deaf': False, 'hard': False, 'mark': False, 'paired': False, 'shock': False, 'soft': False, 'sonorus': True, 'vowel': False} >> EnglishLetter('d').classify() {'consonant': True, 'deaf': False, 'hard': True, 'mark': False, 'paired': False, 'shock': False, 'soft': False, 'sonorus': True, 'vowel': False}
Ahora podemos definir funciones simples para calcular estadísticas, por ejemplo:
def starts_with_letter(word, letter_type='vowel'): """ , . :param word: :param letter_type: 'vowel' 'consonant'. . :return: Boolean """ if len(word) == 0: return False return Letter(word[0]).classify()[letter_type] def count_letter_type(word): """ . :param word: :param debug: :return: :obj:`dict` of :obj:`str` => :int:count """ count = { 'consonant': 0, 'deaf': 0, 'hard': 0, 'mark': 0, 'paired': 0, 'shock': 0, 'soft': 0, 'sonorus': 0, 'vowel': 0 } for letter in word: classes = Letter(letter).classify() for key in count.keys(): if classes[key]: count[key] += 1 return count
Usando estas funciones, ya podemos obtener los primeros signos:
from feature_engineering import * >> print(" («»): ", len("")) («»): 5 >> print(" («») : ", starts_with_letter('', 'vowel')) («») : False >> print(" («») : ", starts_with_letter('', 'consonant')) («») : True >> count_Harry = count_letter_type("") >> print (" («»): ", count_Harry['paired']) («»): 1
Estrictamente hablando, con la ayuda de estas funciones podemos obtener una representación vectorial de la cadena, es decir, obtenemos la asignación:
Ahora podemos presentar nuestros datos en forma de un conjunto de datos que se puede ingresar al algoritmo de aprendizaje automático:
>> from data_loaders import load_processed_data >> hogwarts_df = load_processed_data() >> hogwarts_df.head()

Además, como resultado, obtenemos los siguientes síntomas para cada estudiante:
>> hogwarts_df[hogwarts_df.columns].dtypes
Señales recibidas name object surname object is_english bool name_starts_with_vowel bool name_starts_with_consonant bool name_ends_with_vowel bool name_ends_with_consonant bool name_length int64 name_vowels_count int64 name_double_vowels_count int64 name_consonant_count int64 name_double_consonant_count int64 name_paired_count int64 name_deaf_count int64 name_sonorus_count int64 surname_starts_with_vowel bool surname_starts_with_consonant bool surname_ends_with_vowel bool surname_ends_with_consonant bool surname_length int64 surname_vowels_count int64 surname_double_vowels_count int64 surname_consonant_count int64 surname_double_consonant_count int64 surname_paired_count int64 surname_deaf_count int64 surname_sonorus_count int64 is_griffindor int64 is_hufflpuff int64 is_ravenclaw int64 is_slitherin int64 dtype: object
Las últimas 4 columnas están orientadas: contienen información sobre en qué facultad está inscrito un estudiante.
Entrenamiento de algoritmos
En pocas palabras, los algoritmos se entrenan como las personas: cometen errores y aprenden de ellos. Para comprender cuánto cometieron un error, los algoritmos usan funciones de error (funciones de pérdida, función de pérdida en inglés ).
Como regla general, el proceso de aprendizaje es muy simple y consta de varios pasos:
- Haz una predicción.
- Califica el error.
- Corregir los parámetros del modelo.
- Repita 1-3 hasta que se alcance el objetivo, el proceso se detenga o finalicen los datos.
Califique la calidad del modelo resultante.
En la práctica, por supuesto, todo es un poco más complicado. Por ejemplo, existe el fenómeno del sobreajuste : el algoritmo puede recordar literalmente qué características corresponden a la respuesta y, por lo tanto, empeorar el resultado para objetos que no son similares a aquellos en los que fue entrenado. Para evitar esto, hay varias técnicas y hacks.
Como se mencionó anteriormente, resolveremos 4 problemas: uno para cada facultad. Por lo tanto, prepararemos datos para Slytherin:
Mientras aprende, el algoritmo compara constantemente sus resultados con datos reales, ya que esta parte del conjunto de datos se asigna para validación. La regla del buen tono también se considera para evaluar el resultado del algoritmo en datos individuales que el algoritmo no vio en absoluto. Por lo tanto, ahora dividimos la muestra en la proporción de 70/30 y entrenamos el primer algoritmo:
from sklearn.cross_validation import train_test_split from sklearn.ensemble import RandomForestClassifier
Listo Ahora, si envía datos a la entrada de este modelo, producirá un resultado. Esto es divertido, así que antes de nada verificaremos si la modelo en Harry reconoce al Slytherin. Para hacer esto, primero prepare las funciones para obtener la predicción del algoritmo:
Ver código from data_loaders import parse_line_to_hogwarts_df import pandas as pd def get_single_student_features (name): """ :param name: string :return: pd.DataFrame """ featurized_person_df = parse_line_to_hogwarts_df(name) person_df = pd.DataFrame(featurized_person_df, columns=[ 'name', 'surname', 'is_english', 'name_starts_with_vowel', 'name_starts_with_consonant', 'name_ends_with_vowel', 'name_ends_with_consonant', 'name_length', 'name_vowels_count', 'name_double_vowels_count', 'name_consonant_count', 'name_double_consonant_count', 'name_paired_count', 'name_deaf_count', 'name_sonorus_count', 'surname_starts_with_vowel', 'surname_starts_with_consonant', 'surname_ends_with_vowel', 'surname_ends_with_consonant', 'surname_length', 'surname_vowels_count', 'surname_double_vowels_count', 'surname_consonant_count', 'surname_double_consonant_count', 'surname_paired_count', 'surname_deaf_count', 'surname_sonorus_count', ], index=[0] ) featurized_person = person_df.drop( ['name', 'surname'], axis = 1 ) return featurized_person def get_predictions_vector (model, person): """ :param model: :param person: string :return: list """ encoded_person = get_single_student_features(person) return model.predict_proba(encoded_person)[0]
Ahora configuremos un pequeño conjunto de datos de prueba para considerar los resultados del algoritmo.
def score_testing_dataset (model): """ . :param model: """ testing_dataset = [ " ", "Kirill Malev", " ", "Harry Potter", " ", " ","Severus Snape", " ", "Tom Riddle", " ", "Salazar Slytherin"] for name in testing_dataset: print ("{} — {}".format(name, get_predictions_vector(model, name)[1])) score_testing_dataset(rfc_model)
— 0.5 Kirill Malev — 0.5 — 0.0 Harry Potter — 0.0 — 0.75 — 0.9 Severus Snape — 0.5 — 0.2 Tom Riddle — 0.5 — 0.2 Salazar Slytherin — 0.3
Los resultados fueron dudosos. Incluso el fundador de la facultad no estaría en su facultad, según este modelo. Por lo tanto, debe evaluar la calidad estricta: mire las métricas que le preguntamos al principio:
from sklearn.metrics import accuracy_score, roc_auc_score, classification_report predictions = rfc_model.predict(X_test) print("Classification report: ") print(classification_report(y_test, predictions)) print("Accuracy for Random Forest Model: %.2f" % (accuracy_score(y_test, predictions) * 100)) print("ROC AUC from first Random Forest Model: %.2f" % (roc_auc_score(y_test, predictions)))
Classification report: precision recall f1-score support 0 0.66 0.88 0.75 168 1 0.38 0.15 0.21 89 avg / total 0.56 0.62 0.56 257 Accuracy for Random Forest Model: 62.26 ROC AUC from first Random Forest Model: 0.51
No es sorprendente que los resultados fueran tan dudosos: el AUC ROC de aproximadamente 0,51 sugiere que el modelo predice un poco mejor que un lanzamiento de moneda.
Probar los resultados. Métricas de calidad
Usando un ejemplo anterior, observamos cómo se entrena 1 algoritmo que admite interfaces sklearn. El resto se entrena exactamente de la misma manera, por lo que solo podemos entrenar todos los algoritmos y elegir el mejor en cada caso.

Esto no es complicado, para cada algoritmo entrenamos 1 con configuraciones estándar, y también entrenamos un conjunto completo, clasificando a través de varias opciones que afectan la calidad del algoritmo. Esta etapa se llama Model Tuning u Hyperparameter Optimization y su esencia es muy simple: se selecciona el conjunto de configuraciones que da el mejor resultado.
from model_training import train_classifiers from data_loaders import load_processed_data import warnings warnings.filterwarnings('ignore')
— 0.09437856871661066 Kirill Malev — 0.20820536334902712 — 0.07550095601699099 Harry Potter — 0.07683794773639624 — 0.9414529336862744 — 0.9293671807790949 Severus Snape — 0.6576783576162999 — 0.18577792617672767 Tom Riddle — 0.8351835484058869 — 0.25930925139546795 Salazar Slytherin — 0.24008788903854789
Los números en esta versión se ven subjetivamente mejor que en el pasado, pero aún no son lo suficientemente buenos para un perfeccionista interno. Por lo tanto, bajaremos un nivel más profundo y volveremos al sentido del producto de nuestra tarea: necesitamos predecir la facultad más probable, a la cual el héroe será determinado por el sombrero de distribución. Esto significa que necesitas entrenar modelos para cada una de las facultades.

>> from model_training import train_all_models
Larga conclusión de resultados y resultados de regresión multinomial SVM Default Report Accuracy for SVM Default: 73.93 ROC AUC for SVM Default: 0.53 Tuned SVM Report Accuracy for Tuned SVM: 72.37 ROC AUC for Tuned SVM: 0.50 KNN Default Report Accuracy for KNN Default: 70.04 ROC AUC for KNN Default: 0.58 Tuned KNN Report Accuracy for Tuned KNN: 69.65 ROC AUC for Tuned KNN: 0.58 XGBoost Default Report Accuracy for XGBoost Default: 70.43 ROC AUC for XGBoost Default: 0.54 Tuned XGBoost Report Accuracy for Tuned XGBoost: 68.09 ROC AUC for Tuned XGBoost: 0.56 Random Forest Default Report Accuracy for Random Forest Default: 73.93 ROC AUC for Random Forest Default: 0.62 Tuned Random Forest Report Accuracy for Tuned Random Forest: 74.32 ROC AUC for Tuned Random Forest: 0.54 Extra Trees Default Report Accuracy for Extra Trees Default: 69.26 ROC AUC for Extra Trees Default: 0.57 Tuned Extra Trees Report Accuracy for Tuned Extra Trees: 73.54 ROC AUC for Tuned Extra Trees: 0.55 LGBM Default Report Accuracy for LGBM Default: 70.82 ROC AUC for LGBM Default: 0.62 Tuned LGBM Report Accuracy for Tuned LGBM: 74.71 ROC AUC for Tuned LGBM: 0.53 RGF Default Report Accuracy for RGF Default: 70.43 ROC AUC for RGF Default: 0.58 Tuned RGF Report Accuracy for Tuned RGF: 71.60 ROC AUC for Tuned RGF: 0.60 FRGF Default Report Accuracy for FRGF Default: 68.87 ROC AUC for FRGF Default: 0.59 Tuned FRGF Report Accuracy for Tuned FRGF: 69.26 ROC AUC for Tuned FRGF: 0.59 SVM Default Report Accuracy for SVM Default: 70.43 ROC AUC for SVM Default: 0.50 Tuned SVM Report Accuracy for Tuned SVM: 71.60 ROC AUC for Tuned SVM: 0.50 KNN Default Report Accuracy for KNN Default: 63.04 ROC AUC for KNN Default: 0.49 Tuned KNN Report Accuracy for Tuned KNN: 65.76 ROC AUC for Tuned KNN: 0.50 XGBoost Default Report Accuracy for XGBoost Default: 69.65 ROC AUC for XGBoost Default: 0.54 Tuned XGBoost Report Accuracy for Tuned XGBoost: 68.09 ROC AUC for Tuned XGBoost: 0.50 Random Forest Default Report Accuracy for Random Forest Default: 66.15 ROC AUC for Random Forest Default: 0.51 Tuned Random Forest Report Accuracy for Tuned Random Forest: 70.43 ROC AUC for Tuned Random Forest: 0.50 Extra Trees Default Report Accuracy for Extra Trees Default: 64.20 ROC AUC for Extra Trees Default: 0.49 Tuned Extra Trees Report Accuracy for Tuned Extra Trees: 70.82 ROC AUC for Tuned Extra Trees: 0.51 LGBM Default Report Accuracy for LGBM Default: 67.70 ROC AUC for LGBM Default: 0.56 Tuned LGBM Report Accuracy for Tuned LGBM: 70.82 ROC AUC for Tuned LGBM: 0.50 RGF Default Report Accuracy for RGF Default: 66.54 ROC AUC for RGF Default: 0.52 Tuned RGF Report Accuracy for Tuned RGF: 65.76 ROC AUC for Tuned RGF: 0.53 FRGF Default Report Accuracy for FRGF Default: 65.76 ROC AUC for FRGF Default: 0.53 Tuned FRGF Report Accuracy for Tuned FRGF: 69.65 ROC AUC for Tuned FRGF: 0.52 SVM Default Report Accuracy for SVM Default: 74.32 ROC AUC for SVM Default: 0.50 Tuned SVM Report Accuracy for Tuned SVM: 74.71 ROC AUC for Tuned SVM: 0.51 KNN Default Report Accuracy for KNN Default: 69.26 ROC AUC for KNN Default: 0.48 Tuned KNN Report Accuracy for Tuned KNN: 73.15 ROC AUC for Tuned KNN: 0.49 XGBoost Default Report Accuracy for XGBoost Default: 72.76 ROC AUC for XGBoost Default: 0.49 Tuned XGBoost Report Accuracy for Tuned XGBoost: 74.32 ROC AUC for Tuned XGBoost: 0.50 Random Forest Default Report Accuracy for Random Forest Default: 73.93 ROC AUC for Random Forest Default: 0.52 Tuned Random Forest Report Accuracy for Tuned Random Forest: 74.32 ROC AUC for Tuned Random Forest: 0.50 Extra Trees Default Report Accuracy for Extra Trees Default: 73.93 ROC AUC for Extra Trees Default: 0.52 Tuned Extra Trees Report Accuracy for Tuned Extra Trees: 73.93 ROC AUC for Tuned Extra Trees: 0.50 LGBM Default Report Accuracy for LGBM Default: 73.54 ROC AUC for LGBM Default: 0.52 Tuned LGBM Report Accuracy for Tuned LGBM: 74.32 ROC AUC for Tuned LGBM: 0.50 RGF Default Report Accuracy for RGF Default: 73.54 ROC AUC for RGF Default: 0.51 Tuned RGF Report Accuracy for Tuned RGF: 73.93 ROC AUC for Tuned RGF: 0.50 FRGF Default Report Accuracy for FRGF Default: 73.93 ROC AUC for FRGF Default: 0.53 Tuned FRGF Report Accuracy for Tuned FRGF: 73.93 ROC AUC for Tuned FRGF: 0.50 SVM Default Report Accuracy for SVM Default: 80.54 ROC AUC for SVM Default: 0.50 Tuned SVM Report Accuracy for Tuned SVM: 80.93 ROC AUC for Tuned SVM: 0.52 KNN Default Report Accuracy for KNN Default: 78.60 ROC AUC for KNN Default: 0.50 Tuned KNN Report Accuracy for Tuned KNN: 80.16 ROC AUC for Tuned KNN: 0.51 XGBoost Default Report Accuracy for XGBoost Default: 80.54 ROC AUC for XGBoost Default: 0.50 Tuned XGBoost Report Accuracy for Tuned XGBoost: 77.04 ROC AUC for Tuned XGBoost: 0.52 Random Forest Default Report Accuracy for Random Forest Default: 77.43 ROC AUC for Random Forest Default: 0.49 Tuned Random Forest Report Accuracy for Tuned Random Forest: 80.54 ROC AUC for Tuned Random Forest: 0.50 Extra Trees Default Report Accuracy for Extra Trees Default: 76.26 ROC AUC for Extra Trees Default: 0.48 Tuned Extra Trees Report Accuracy for Tuned Extra Trees: 78.60 ROC AUC for Tuned Extra Trees: 0.50 LGBM Default Report Accuracy for LGBM Default: 75.49 ROC AUC for LGBM Default: 0.51 Tuned LGBM Report Accuracy for Tuned LGBM: 80.54 ROC AUC for Tuned LGBM: 0.50 RGF Default Report Accuracy for RGF Default: 78.99 ROC AUC for RGF Default: 0.52 Tuned RGF Report Accuracy for Tuned RGF: 75.88 ROC AUC for Tuned RGF: 0.55 FRGF Default Report Accuracy for FRGF Default: 76.65 ROC AUC for FRGF Default: 0.50 # ,
from sklearn.linear_model import LogisticRegression clf = LogisticRegression(random_state=0, solver='lbfgs', multi_class='multinomial') hogwarts_df = load_processed_data_multi()
— [0.3602361 0.16166944 0.16771712 0.31037733] Kirill Malev — [0.47473072 0.16051924 0.13511385 0.22963619] — [0.38697926 0.19330242 0.17451052 0.2452078 ] Harry Potter — [0.40245098 0.16410043 0.16023278 0.27321581] — [0.13197025 0.16438855 0.17739254 0.52624866] — [0.17170203 0.1205678 0.14341742 0.56431275] Severus Snape — [0.15558044 0.21589378 0.17370406 0.45482172] — [0.39301231 0.07397324 0.1212741 0.41174035] Tom Riddle — [0.26623969 0.14194379 0.1728505 0.41896601] — [0.24843037 0.21632736 0.21532696 0.3199153 ] Salazar Slytherin — [0.09359144 0.26735897 0.2742305 0.36481909]
Y confusion_matrix:
confusion_matrix(clf.predict(X_data), y)
array([[144, 68, 64, 78], [ 8, 9, 8, 6], [ 22, 18, 31, 20], [ 77, 73, 78, 151]])
def get_predctions_vector (models, person): predictions = [get_predictions_vector (model, person)[1] for model in models] return { 'slitherin': predictions[0], 'griffindor': predictions[1], 'ravenclaw': predictions[2], 'hufflpuff': predictions[3] } def score_testing_dataset (models): testing_dataset = [ " ", "Kirill Malev", " ", "Harry Potter", " ", " ","Severus Snape", " ", "Tom Riddle", " ", "Salazar Slytherin"] data = [] for name in testing_dataset: predictions = get_predctions_vector(models, name) predictions['name'] = name data.append(predictions) scoring_df = pd.DataFrame(data, columns=['name', 'slitherin', 'griffindor', 'hufflpuff', 'ravenclaw']) return scoring_df
name slitherin griffindor hufflpuff ravenclaw 0 0.349084 0.266909 0.110311 0.091045 1 Kirill Malev 0.289914 0.376122 0.384986 0.103056 2 0.338258 0.400841 0.016668 0.124825 3 Harry Potter 0.245377 0.357934 0.026287 0.154592 4 0.917423 0.126997 0.176640 0.096570 5 0.969693 0.106384 0.150146 0.082195 6 Severus Snape 0.663732 0.259189 0.290252 0.074148 7 0.268466 0.579401 0.007900 0.083195 8 Tom Riddle 0.639731 0.541184 0.084395 0.156245 9 0.653595 0.147506 0.172940 0.137134 10 Salazar Slytherin 0.647399 0.169964 0.095450 0.26126

, . ROC AUC , 0.5. , :
, , , , XGBoost CV , .
Importante! , 70% . , 4 .
from model_training import train_production_models from xgboost import XGBClassifier best_models = [] for i in range (0,4): best_models.append(XGBClassifier(base_score=0.5, booster='gbtree', colsample_bylevel=1, colsample_bytree=0.7, gamma=0, learning_rate=0.05, max_delta_step=0, max_depth=6, min_child_weight=11, missing=-999, n_estimators=1000, n_jobs=1, nthread=4, objective='binary:logistic', random_state=0, reg_alpha=0, reg_lambda=1, scale_pos_weight=1, seed=1337, silent=1, subsample=0.8)) slitherin_model, griffindor_model, ravenclaw_model, hufflpuff_model = \ train_production_models(best_models) top_models = slitherin_model, griffindor_model, ravenclaw_model, hufflpuff_model score_testing_dataset(top_models)
name slitherin griffindor hufflpuff ravenclaw 0 0.273713 0.372337 0.065923 0.279577 1 Kirill Malev 0.401603 0.761467 0.111068 0.023902 2 0.031540 0.616535 0.196342 0.217829 3 Harry Potter 0.183760 0.422733 0.119393 0.173184 4 0.945895 0.021788 0.209820 0.019449 5 0.950932 0.088979 0.084131 0.012575 6 Severus Snape 0.634035 0.088230 0.249871 0.036682 7 0.426440 0.431351 0.028444 0.083636 8 Tom Riddle 0.816804 0.136530 0.069564 0.035500 9 0.409634 0.213925 0.028631 0.252723 10 Salazar Slytherin 0.824590 0.067910 0.111147 0.085710
, , .
, , . .
import pickle pickle.dump(slitherin_model, open("../output/slitherin.xgbm", "wb")) pickle.dump(griffindor_model, open("../output/griffindor.xgbm", "wb")) pickle.dump(ravenclaw_model, open("../output/ravenclaw.xgbm", "wb")) pickle.dump(hufflpuff_model, open("../output/hufflpuff.xgbm", "wb"))
, . , , , .
, , . , . , Data Scientist — -.
:
, docker-, python-. , flask.
from __future__ import print_function
Dockerfile:
FROM datmo/python-base:cpu-py35
:
docker build -t talking_hat . && docker rm talking_hat && docker run --name talking_hat -p 5000:5000 talking_hat
— . , Apache Benchmark . , . — .
$ ab -p data.json -T application/json -c 50 -n 10000 http://0.0.0.0:5000/predict
ab This is ApacheBench, Version 2.3 <$Revision: 1807734 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking 0.0.0.0 (be patient) Completed 1000 requests Completed 2000 requests Completed 3000 requests Completed 4000 requests Completed 5000 requests Completed 6000 requests Completed 7000 requests Completed 8000 requests Completed 9000 requests Completed 10000 requests Finished 10000 requests Server Software: Werkzeug/0.14.1 Server Hostname: 0.0.0.0 Server Port: 5000 Document Path: /predict Document Length: 141 bytes Concurrency Level: 50 Time taken for tests: 238.552 seconds Complete requests: 10000 Failed requests: 0 Total transferred: 2880000 bytes Total body sent: 1800000 HTML transferred: 1410000 bytes Requests per second: 41.92 [#/sec] (mean) Time per request: 1192.758 [ms] (mean) Time per request: 23.855 [ms] (mean, across all concurrent requests) Transfer rate: 11.79 [Kbytes/sec] received 7.37 kb/s sent 19.16 kb/s total Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.1 0 3 Processing: 199 1191 352.5 1128 3352 Waiting: 198 1190 352.5 1127 3351 Total: 202 1191 352.5 1128 3352 Percentage of the requests served within a certain time (ms) 50% 1128 66% 1277 75% 1378 80% 1451 90% 1668 95% 1860 98% 2096 99% 2260 100% 3352 (longest request)
, :
def prod_predict_classes_for_name (full_name): <...> predictions = get_predctions_vector([ app.slitherin_model, app.griffindor_model, app.ravenclaw_model, app.hufflpuff_model ], person_df.drop(['name', 'surname'], axis=1)) return { 'slitherin': float(predictions[0][1]), 'griffindor': float(predictions[1][1]), 'ravenclaw': float(predictions[2][1]), 'hufflpuff': float(predictions[3][1]) } def create_app(): <...> with app.app_context(): app.slitherin_model = pickle.load(open("models/slitherin.xgbm", "rb")) app.griffindor_model = pickle.load(open("models/griffindor.xgbm", "rb")) app.ravenclaw_model = pickle.load(open("models/ravenclaw.xgbm", "rb")) app.hufflpuff_model = pickle.load(open("models/hufflpuff.xgbm", "rb")) return app
:
$ docker build -t talking_hat . && docker rm talking_hat && docker run --name talking_hat -p 5000:5000 talking_hat $ ab -p data.json -T application/json -c 50 -n 10000 http://0.0.0.0:5000/predict
ab This is ApacheBench, Version 2.3 <$Revision: 1807734 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking 0.0.0.0 (be patient) Completed 1000 requests Completed 2000 requests Completed 3000 requests Completed 4000 requests Completed 5000 requests Completed 6000 requests Completed 7000 requests Completed 8000 requests Completed 9000 requests Completed 10000 requests Finished 10000 requests Server Software: Werkzeug/0.14.1 Server Hostname: 0.0.0.0 Server Port: 5000 Document Path: /predict Document Length: 141 bytes Concurrency Level: 50 Time taken for tests: 219.812 seconds Complete requests: 10000 Failed requests: 3 (Connect: 0, Receive: 0, Length: 3, Exceptions: 0) Total transferred: 2879997 bytes Total body sent: 1800000 HTML transferred: 1409997 bytes Requests per second: 45.49 [#/sec] (mean) Time per request: 1099.062 [ms] (mean) Time per request: 21.981 [ms] (mean, across all concurrent requests) Transfer rate: 12.79 [Kbytes/sec] received 8.00 kb/s sent 20.79 kb/s total Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.1 0 2 Processing: 235 1098 335.2 1035 3464 Waiting: 235 1097 335.2 1034 3462 Total: 238 1098 335.2 1035 3464 Percentage of the requests served within a certain time (ms) 50% 1035 66% 1176 75% 1278 80% 1349 90% 1541 95% 1736 98% 1967 99% 2141 100% 3464 (longest request)
. . , .
Conclusión
, . - .
, :
- feature engineering- ( ), , Soundex .
- PyTorch . , , .
- flask Quart , , .
- - -, .
, , . , !
Este artículo no habría sido publicado sin la comunidad Open Data Science, que reúne a un gran número de expertos de habla rusa en el campo del análisis de datos.