Ingeniería

Entrenar, evaluar, monitorear, inferir: Machine Learning integral en Elastic

Los pipelines de Machine Learning evolucionaron mucho en los últimos años. Con una amplia variedad de herramientas y marcos de trabajo disponibles para simplificar la creación, la capacitación y el despliegue, el plazo de entrega del desarrollo de modelos de Machine Learning mejoró drásticamente. Sin embargo, incluso con todas estas simplificaciones, sigue habiendo una curva de aprendizaje empinada asociada con muchas de estas herramientas. Pero no con Elastic.

Para usar Machine Learning en el Elastic Stack, lo único que realmente necesitas es que tus datos se almacenen en Elasticsearch. Una vez allí, extraer información valiosa de tus datos es tan simple como hacer clic en algunos botones en Kibana. Machine Learning está incorporado en el Elastic Stack, lo que te permite crear de forma fácil e intuitiva un pipeline de Machine Learning integral completamente operativo. Y en este blog haremos exactamente eso.

¿Por qué Elastic?

Ser una empresa de búsqueda significa que Elastic se creó para manejar de manera eficiente grandes cantidades de datos. La búsqueda y agregación de datos para análisis es fácil e intuitiva gracias a Query DSL de Elasticsearch. Los sets de datos grandes pueden visualizarse de varias formas en Kibana. La interfaz de Machine Learning de Elastic permite realizar de manera fácil la selección de características y modelos, el entrenamiento de modelos y el ajuste de hiperparámetros. Una vez que hayas entrenado y ajustado tu modelo, Kibana también se puede usar para evaluar y monitorear visualmente los modelos. Esto hace que el Elastic Stack sea el punto único perfecto para Machine Learning de nivel de producción.

Set de datos de ejemplo: EMBER 2018

Demostraremos Machine Learning integral en el Elastic Stack usando el set de datos EMBER, publicado por Endgame para permitir la detección de malware con características estáticas derivadas de archivos ejecutables portables (PE). Para esta demostración, usaremos el set de datos EMBER (Endgame Malware BEnchmark for Research, punto de referencia para la investigación de malware de Endgame) 2018 data set, una recopilación open source de un millón de muestras. Cada muestra incluye el hash sha256 del archivo de muestra, el mes en que se vio el archivo por primera vez, una etiqueta y las características derivadas del archivo. 

Para este experimento, seleccionaremos 300 000 muestras (150 000 maliciosas y 150 000 benignas) del set de datos EMBER 2018. Primero debemos seleccionar algunas características para poder realizar el aprendizaje supervisado con las muestras. Las características en el set de datos son características estáticas derivadas del contenido de archivos binarios. Decidimos experimentar con la información general, de encabezado de archivo y de sección, cadenas e histogramas de bytes, para estudiar el rendimiento del modelo con el uso de diferentes subconjuntos de características del set de datos EMBER 2018. 

Machine Learning integral en el Elastic Stack: Un repaso

Para esta demostración, usaremos Python Elasticsearch Client para insertar datos en Elasticsearch, la característica de analíticas de cuadros de datos de Machine Learning de Elastic para crear trabajos de entrenamiento y Kibana para monitorear visualmente los modelos después del entrenamiento. 

Crearemos dos trabajos supervisados, uno con la información general, de encabezado de archivo y de sección, y cadenas, y el otro con solo histogramas de bytes como características. El objetivo es demostrar el entrenamiento simultáneo de varios modelos en el Stack y la posterior visualización de diversos modelos candidatos.

Configuración de Elasticsearch

Con el objetivo de usar Machine Learning en el Elastic Stack, primero debemos activar Elasticsearch con un nodo de Machine Learning. Para hacerlo, podemos iniciar una prueba gratuita de 14 días de Elastic Cloud que está disponible para todos. Nuestro ejemplo de despliegue tiene la configuración siguiente:

  • Cloud Platform: Amazon Web Services
  • Region: US West (N. California)
  • Optimization: I/O Optimized
  • Customize Deployment: Enable Machine Learning

También debemos crear claves de API y asignarles privilegios adecuados para interactuar con Elasticsearch usando Python Elasticsearch Client. En nuestro repaso, insertaremos datos en el índice ember_ml, por lo que crearemos una clave de la siguiente manera:

POST /_security/api_key 
{ 
  "name": "my_awesome_key", 
  "role_descriptors": { 
    "role_1": { 
      "cluster": ["all"], 
      "index": [ 
        { 
          "names": ["ember_*"], 
          "privileges": ["all"] 
        } 
      ] 
    } 
  } 
}

Ingesta de datos

Una vez configurada nuestra instancia de Elasticsearch, comenzaremos por ingestar datos en un índice de Elasticsearch. Primero, crearemos un índice llamado ember_ml y después ingestaremos en este los documentos que conforman nuestro set de datos usando Python Elasticsearch Client. Ingestaremos todas las características requeridas para ambos modelos en un solo índice, usando el asistente de bulk de transmisión para ingestar documentos de forma masiva en Elasticsearch. El código Python para crear el índice ember_ml e ingestar documentos de forma masiva en él es el siguiente:

import elasticsearch 
import certifi 
from elasticsearch import Elasticsearch, helpers 
# Larga lista de documentos para insertar en Elasticsearch, se muestra uno como ejemplo 
documents = [ 
  { 
    "_index": "ember_ml", 
    "_id": "771434adbbfa2ff5740eb91d9deb51828e0f4b060826b590cd9fd8dd46ee0d40", 
    "_source": { 
      "sha256": "771434adbbfa2ff5740eb91d9deb51828e0f4b060826b590cd9fd8dd46ee0d4b", 
      "appeared": "2018-01-06 00:00:00", 
      "label": 1, 
      "byte_0": 0.1826012283563614, 
      "byte_1": 0.006036404054611921, 
      "byte_2": 0.003830794943496585, 
      "byte_3": 0.004225482698529959, 
      "byte_4": 0.004388001281768084, 
      "byte_5": 0.0036218424793332815, 
      "byte_6": 0.0035289747174829245, 
      "byte_7": 0.004666604567319155, 
      "byte_8": 0.004225482698529959, 
      "byte_9": 0.0029253342654556036, 
      "byte_10": 0.0034361069556325674, 
      "byte_11": 0.003993313293904066, 
      "byte_12": 0.004039747174829245, 
      "byte_13": 0.0029253342654556036, 
      "byte_14": 0.0030182020273059607, 
      "byte_15": 0.0036450594197958708, 
      "byte_16": 0.004573736805468798, 
      "byte_17": 0.002693164860829711, 
      "byte_18": 0.002507429337128997, 
      "byte_19": 0.0026699479203671217, 
      "byte_20": 0.003505757777020335, 
      "byte_21": 0.0022056091111153364, 
      "byte_22": 0.0032503714319318533, 
      "byte_23": 0.0025770801585167646, 
      "byte_24": 0.005363112781196833, 
      "byte_25": 0.002600297098979354, 
      "byte_26": 0.0025538632180541754, 
      "byte_27": 0.0031807206105440855, 
      "byte_28": 0.0034593238960951567, 
      "byte_29": 0.0022288260515779257, 
      "byte_30": 0.002507429337128997, 
      "byte_31": 0.0025770801585167646, 
      "byte_32": 0.004921990912407637, 
      "byte_33": 0.0028092495631426573, 
      "byte_34": 0.0017877042992040515, 
      "byte_35": 0.0033664561342447996, 
      "byte_36": 0.002437778515741229, 
      "byte_37": 0.0021359582897275686, 
      "byte_38": 0.0016716195968911052, 
      "byte_39": 0.0020430905278772116, 
      "byte_40": 0.003227154491469264, 
      "byte_41": 0.0025770801585167646, 
      "byte_42": 0.0017644873587414622, 
      "byte_43": 0.0032039375510066748, 
      "byte_44": 0.003296805312857032, 
      "byte_45": 0.003134286729618907, 
      "byte_46": 0.0028324665036052465, 
      "byte_47": 0.003505757777020335, 
      "byte_48": 0.0038772288244217634, 
      "byte_49": 0.0035521916579455137, 
      "byte_50": 0.0031110697891563177, 
      "byte_51": 0.00417904881760478, 
      "byte_52": 0.004225482698529959, 
      "byte_53": 0.0032503714319318533, 
      "byte_54": 0.0035289747174829245, 
      "byte_55": 0.003320022253319621, 
      "byte_56": 0.0030878528486937284, 
      "byte_57": 0.003575408598408103, 
      "byte_58": 0.002182392170652747, 
      "byte_59": 0.0029021173249930143, 
      "byte_60": 0.002344910753890872, 
      "byte_61": 0.0020430905278772116, 
      "byte_62": 0.0015555348945781589, 
      "byte_63": 0.0020198735874146223, 
      "byte_64": 0.004016530234366655, 
      "byte_65": 0.004457652103155851, 
      "byte_66": 0.0036450594197958708, 
      "byte_67": 0.0036218424793332815, 
      "byte_68": 0.0038075780030339956, 
      "byte_69": 0.0033432391937822104, 
      "byte_70": 0.004852340091019869, 
      "byte_71": 0.004039747174829245, 
      "byte_72": 0.00480590621009469, 
      "byte_73": 0.002971768146380782, 
      "byte_74": 0.002693164860829711, 
      "byte_75": 0.0039468794129788876, 
      "byte_76": 0.0036450594197958708, 
      "byte_77": 0.0034361069556325674, 
      "byte_78": 0.0028324665036052465, 
      "byte_79": 0.0028324665036052465, 
      "byte_80": 0.005664933007210493, 
      "byte_81": 0.0029949850868433714, 
      "byte_82": 0.0031110697891563177, 
      "byte_83": 0.004527302924543619, 
      "byte_84": 0.003923662472516298, 
      "byte_85": 0.0029949850868433714, 
      "byte_86": 0.004016530234366655, 
      "byte_87": 0.004573736805468798, 
      "byte_88": 0.004109397996217012, 
      "byte_89": 0.003296805312857032, 
      "byte_90": 0.0033664561342447996, 
      "byte_91": 0.0034593238960951567, 
      "byte_92": 0.0031110697891563177, 
      "byte_93": 0.0022984768729656935, 
      "byte_94": 0.0022288260515779257, 
      "byte_95": 0.002275259932503104, 
      "byte_96": 0.002855683444067836, 
      "byte_97": 0.0035986255388706923, 
      "byte_98": 0.0026699479203671217, 
      "byte_99": 0.0037843610625714064, 
      "byte_100": 0.004364784341305494, 
      "byte_101": 0.004016530234366655, 
      "byte_102": 0.004713038448244333, 
      "byte_103": 0.003505757777020335, 
      "byte_104": 0.005479197483509779, 
      "byte_105": 0.0032503714319318533, 
      "byte_106": 0.00366827636025846, 
      "byte_107": 0.004016530234366655, 
      "byte_108": 0.005061292555183172, 
      "byte_109": 0.005014858674257994, 
      "byte_110": 0.0039468794129788876, 
      "byte_111": 0.004109397996217012, 
      "byte_112": 0.004596953745931387, 
      "byte_113": 0.0021127413492649794, 
      "byte_114": 0.0046433876268565655, 
      "byte_115": 0.004086181055754423, 
      "byte_116": 0.005664933007210493, 
      "byte_117": 0.005293461959809065, 
      "byte_118": 0.0039468794129788876, 
      "byte_119": 0.0038075780030339956, 
      "byte_120": 0.0035289747174829245, 
      "byte_121": 0.004480869043618441, 
      "byte_122": 0.00183413818012923, 
      "byte_123": 0.0032503714319318533, 
      "byte_124": 0.0027163818012923002, 
      "byte_125": 0.002066307468339801, 
      "byte_126": 0.003505757777020335, 
      "byte_127": 0.002252042992040515, 
      "byte_128": 0.0033432391937822104, 
      "byte_129": 0.0032039375510066748, 
      "byte_130": 0.001741270418278873, 
      "byte_131": 0.003923662472516298, 
      "byte_132": 0.003830794943496585, 
      "byte_133": 0.0033664561342447996, 
      "byte_134": 0.0034361069556325674, 
      "byte_135": 0.0014162332518026233, 
      "byte_136": 0.002600297098979354, 
      "byte_137": 0.00304141896776855, 
      "byte_138": 0.0022984768729656935, 
      "byte_139": 0.0037147102411836386, 
      "byte_140": 0.0051773772574961185, 
      "byte_141": 0.003296805312857032, 
      "byte_142": 0.0031575036700814962, 
      "byte_143": 0.0015555348945781589, 
      "byte_144": 0.003064635908231139, 
      "byte_145": 0.002693164860829711, 
      "byte_146": 0.0012304977281019092, 
      "byte_147": 0.0015555348945781589, 
      "byte_148": 0.003830794943496585, 
      "byte_149": 0.0028092495631426573, 
      "byte_150": 0.00208952440880239, 
      "byte_151": 0.0014626671327278018, 
      "byte_152": 0.0026699479203671217, 
      "byte_153": 0.004388001281768084, 
      "byte_154": 0.0019502228824421763, 
      "byte_155": 0.0017644873587414622, 
      "byte_156": 0.004086181055754423, 
      "byte_157": 0.0017180534778162837, 
      "byte_158": 0.003412890015169978, 
      "byte_159": 0.002252042992040515, 
      "byte_160": 0.002507429337128997, 
      "byte_161": 0.002437778515741229, 
      "byte_162": 0.002623514039441943, 
      "byte_163": 0.0022288260515779257, 
      "byte_164": 0.0020430905278772116, 
      "byte_165": 0.0022984768729656935, 
      "byte_166": 0.0017180534778162837, 
      "byte_167": 0.0010911960853263736, 
      "byte_168": 0.002159175230190158, 
      "byte_169": 0.0015091010136529803, 
      "byte_170": 0.003227154491469264, 
      "byte_171": 0.0025770801585167646, 
      "byte_172": 0.0027628156822174788, 
      "byte_173": 0.0029253342654556036, 
      "byte_174": 0.0013697993708774447, 
      "byte_175": 0.001648402656428516, 
      "byte_176": 0.003134286729618907, 
      "byte_177": 0.0016019687755033374, 
      "byte_178": 0.002437778515741229, 
      "byte_179": 0.001927005941979587, 
      "byte_180": 0.0027163818012923002, 
      "byte_181": 0.004016530234366655, 
      "byte_182": 0.003227154491469264, 
      "byte_183": 0.00241456157527864, 
      "byte_184": 0.0025538632180541754, 
      "byte_185": 0.00208952440880239, 
      "byte_186": 0.001648402656428516, 
      "byte_187": 0.002275259932503104, 
      "byte_188": 0.0025538632180541754, 
      "byte_189": 0.0028092495631426573, 
      "byte_190": 0.0021359582897275686, 
      "byte_191": 0.0027395987417548895, 
      "byte_192": 0.0030878528486937284, 
      "byte_193": 0.0027395987417548895, 
      "byte_194": 0.00208952440880239, 
      "byte_195": 0.002878900384530425, 
      "byte_196": 0.0021359582897275686, 
      "byte_197": 0.00208952440880239, 
      "byte_198": 0.0027395987417548895, 
      "byte_199": 0.0019734397064894438, 
      "byte_200": 0.003064635908231139, 
      "byte_201": 0.002066307468339801, 
      "byte_202": 0.0012304977281019092, 
      "byte_203": 0.00183413818012923, 
      "byte_204": 0.003389673074707389, 
      "byte_205": 0.00304141896776855, 
      "byte_206": 0.0029021173249930143, 
      "byte_207": 0.0024609954562038183, 
      "byte_208": 0.0029021173249930143, 
      "byte_209": 0.002507429337128997, 
      "byte_210": 0.0022288260515779257, 
      "byte_211": 0.0019734397064894438, 
      "byte_212": 0.0023913446348160505, 
      "byte_213": 0.0017180534778162837, 
      "byte_214": 0.0032735883723944426, 
      "byte_215": 0.0023216938134282827, 
      "byte_216": 0.003412890015169978, 
      "byte_217": 0.0025538632180541754, 
      "byte_218": 0.002530646277591586, 
      "byte_219": 0.004550519865006208, 
      "byte_220": 0.003320022253319621, 
      "byte_221": 0.002437778515741229, 
      "byte_222": 0.003389673074707389, 
      "byte_223": 0.002855683444067836, 
      "byte_224": 0.0031575036700814962, 
      "byte_225": 0.0018109212396666408, 
      "byte_226": 0.002182392170652747, 
      "byte_227": 0.003737927181646228, 
      "byte_228": 0.0036218424793332815, 
      "byte_229": 0.0014626671327278018, 
      "byte_230": 0.0024609954562038183, 
      "byte_231": 0.002600297098979354, 
      "byte_232": 0.0024609954562038183, 
      "byte_233": 0.0015323179541155696, 
      "byte_234": 0.001137629966251552, 
      "byte_235": 0.004341567400842905, 
      "byte_236": 0.004782689269632101, 
      "byte_237": 0.0024609954562038183, 
      "byte_238": 0.0016716195968911052, 
      "byte_239": 0.0028092495631426573, 
      "byte_240": 0.0036218424793332815, 
      "byte_241": 0.00183413818012923, 
      "byte_242": 0.0035289747174829245, 
      "byte_243": 0.002623514039441943, 
      "byte_244": 0.0022984768729656935, 
      "byte_245": 0.001741270418278873, 
      "byte_246": 0.003296805312857032, 
      "byte_247": 0.003412890015169978, 
      "byte_248": 0.003134286729618907, 
      "byte_249": 0.0023913446348160505, 
      "byte_250": 0.0012304977281019092, 
      "byte_251": 0.0067561292089521885, 
      "byte_252": 0.005943536292761564, 
      "byte_253": 0.0031575036700814962, 
      "byte_254": 0.004480869043618441, 
      "byte_255": 0.038958024233579636, 
      "strings_0": 488, 
      "strings_1": 7.477458953857422, 
      "strings_2": 3649, 
      "strings_3": 0.011784050613641739, 
      "strings_4": 0.0043847630731761456, 
      "strings_5": 0.003562619909644127, 
      "strings_6": 0.005206905771046877, 
      "strings_7": 0.004110715351998806, 
      "strings_8": 0.003014524467289448, 
      "strings_9": 0.003562619909644127, 
      "strings_10": 0.005755001213401556, 
      "strings_11": 0.006029048934578896, 
      "strings_12": 0.003014524467289448, 
      "strings_13": 0.0019183338154107332, 
      "strings_14": 0.010961906984448433, 
      "strings_15": 0.006577144376933575, 
      "strings_16": 0.006851192098110914, 
      "strings_17": 0.008769526146352291, 
      "strings_18": 0.013428336940705776, 
      "strings_19": 0.011784050613641739, 
      "strings_20": 0.012058097869157791, 
      "strings_21": 0.014250479638576508, 
      "strings_22": 0.013428336940705776, 
      "strings_23": 0.01315428875386715, 
      "strings_24": 0.01068785972893238, 
      "strings_25": 0.01315428875386715, 
      "strings_26": 0.012880241498351097, 
      "strings_27": 0.010139764286577702, 
      "strings_28": 0.010413811542093754, 
      "strings_29": 0.0027404767461121082, 
      "strings_30": 0.006029048934578896, 
      "strings_31": 0.004658810794353485, 
      "strings_32": 0.0021923815365880728, 
      "strings_33": 0.0027404767461121082, 
      "strings_34": 0.004110715351998806, 
      "strings_35": 0.005755001213401556, 
      "strings_36": 0.01589476503431797, 
      "strings_37": 0.011784050613641739, 
      "strings_38": 0.01397643145173788, 
      "strings_39": 0.010413811542093754, 
      "strings_40": 0.016168814152479172, 
      "strings_41": 0.015346670523285866, 
      "strings_42": 0.012332146055996418, 
      "strings_43": 0.013428336940705776, 
      "strings_44": 0.01452452689409256, 
      "strings_45": 0.00986571703106165, 
      "strings_46": 0.016442861407995224, 
      "strings_47": 0.014798575080931187, 
      "strings_48": 0.012058097869157791, 
      "strings_49": 0.01068785972893238, 
      "strings_50": 0.010413811542093754, 
      "strings_51": 0.015620717778801918, 
      "strings_52": 0.010139764286577702, 
      "strings_53": 0.013428336940705776, 
      "strings_54": 0.015072622336447239, 
      "strings_55": 0.014250479638576508, 
      "strings_56": 0.011510002426803112, 
      "strings_57": 0.012880241498351097, 
      "strings_58": 0.01397643145173788, 
      "strings_59": 0.012332146055996418, 
      "strings_60": 0.01068785972893238, 
      "strings_61": 0.00931762158870697, 
      "strings_62": 0.00986571703106165, 
      "strings_63": 0.005206905771046877, 
      "strings_64": 0.003014524467289448, 
      "strings_65": 0.003014524467289448, 
      "strings_66": 0.003562619909644127, 
      "strings_67": 0.0043847630731761456, 
      "strings_68": 0.01397643145173788, 
      "strings_69": 0.010413811542093754, 
      "strings_70": 0.017539052292704582, 
      "strings_71": 0.017539052292704582, 
      "strings_72": 0.02000548131763935, 
      "strings_73": 0.016442861407995224, 
      "strings_74": 0.014250479638576508, 
      "strings_75": 0.01452452689409256, 
      "strings_76": 0.01260619331151247, 
      "strings_77": 0.011510002426803112, 
      "strings_78": 0.013428336940705776, 
      "strings_79": 0.014798575080931187, 
      "strings_80": 0.016442861407995224, 
      "strings_81": 0.01452452689409256, 
      "strings_82": 0.017813099548220634, 
      "strings_83": 0.015072622336447239, 
      "strings_84": 0.00931762158870697, 
      "strings_85": 0.01452452689409256, 
      "strings_86": 0.014250479638576508, 
      "strings_87": 0.015620717778801918, 
      "strings_88": 0.014250479638576508, 
      "strings_89": 0.012332146055996418, 
      "strings_90": 0.013702384196221828, 
      "strings_91": 0.01397643145173788, 
      "strings_92": 0.00986571703106165, 
      "strings_93": 0.006303096655756235, 
      "strings_94": 0.004110715351998806, 
      "strings_95": 0.0027404767461121082, 
      "strings_96": 0.0027404767461121082, 
      "strings_97": 0.0024664292577654123, 
      "strings_98": 0.007399287540465593, 
      "strings_99": 6.4175848960876465, 
      "strings_100": 0, 
      "strings_101": 0, 
      "strings_102": 0, 
      "strings_103": 3, 
      "general_info_0": 43072, 
      "general_info_1": 110592, 
      "general_info_2": 0, 
      "general_info_3": 0, 
      "general_info_4": 5, 
      "general_info_5": 0, 
      "general_info_6": 1, 
      "general_info_7": 0, 
      "general_info_8": 0, 
      "general_info_9": 0, 
      "file_header_0": 1142459136, 
      "file_header_1": 0, 
      "file_header_2": 0, 
      "file_header_3": 0, 
      "file_header_4": 0, 
      "file_header_5": 0, 
      "file_header_6": 1, 
      "file_header_7": 0, 
      "file_header_8": 0, 
      "file_header_9": 0, 
      "file_header_10": 0, 
      "file_header_11": 0, 
      "file_header_12": 0, 
      "file_header_13": -1, 
      "file_header_14": 0, 
      "file_header_15": -1, 
      "file_header_16": -1, 
      "file_header_17": 0, 
      "file_header_18": 0, 
      "file_header_19": 0, 
      "file_header_20": 0, 
      "file_header_21": 0, 
      "file_header_22": 0, 
      "file_header_23": 0, 
      "file_header_24": 0, 
      "file_header_25": 0, 
      "file_header_26": 0, 
      "file_header_27": 0, 
      "file_header_28": 1, 
      "file_header_29": 0, 
      "file_header_30": 0, 
      "file_header_31": 0, 
      "file_header_32": 0, 
      "file_header_33": 0, 
      "file_header_34": 0, 
      "file_header_35": 0, 
      "file_header_36": 0, 
      "file_header_37": 0, 
      "file_header_38": 0, 
      "file_header_39": 0, 
      "file_header_40": 0, 
      "file_header_41": 0, 
      "file_header_42": -1, 
      "file_header_43": 0, 
      "file_header_44": 0, 
      "file_header_45": 0, 
      "file_header_46": 0, 
      "file_header_47": 0, 
      "file_header_48": 0, 
      "file_header_49": 0, 
      "file_header_50": 0, 
      "file_header_51": 0, 
      "file_header_52": 0, 
      "file_header_53": 2, 
      "file_header_54": 48, 
      "file_header_55": 4, 
      "file_header_56": 0, 
      "file_header_57": 4, 
      "file_header_58": 0, 
      "file_header_59": 32768, 
      "file_header_60": 4096, 
      "file_header_61": 4096, 
      "sections_0": 3, 
      "sections_1": 1, 
      "sections_2": 0, 
      "sections_3": 1, 
      "sections_4": 3, 
      "sections_5": 0, 
      "sections_6": 0, 
      "sections_7": 0, 
      "sections_8": 0, 
      "sections_9": 0, 
      "sections_10": 0, 
      "sections_11": 0, 
      "sections_12": 0, 
      "sections_13": 0, 
      "sections_14": 0, 
      "sections_15": 0, 
      "sections_16": 0, 
      "sections_17": 0, 
      "sections_18": 0, 
      "sections_19": 0, 
      "sections_20": 0, 
      "sections_21": 0, 
      "sections_22": 0, 
      "sections_23": 0, 
      "sections_24": 0, 
      "sections_25": 0, 
      "sections_26": 0, 
      "sections_27": 0, 
      "sections_28": 0, 
      "sections_29": 0, 
      "sections_30": 0, 
      "sections_31": 0, 
      "sections_32": 0, 
      "sections_33": 0, 
      "sections_34": 0, 
      "sections_35": 0, 
      "sections_36": 0, 
      "sections_37": 0, 
      "sections_38": 0, 
      "sections_39": 0, 
      "sections_40": 0, 
      "sections_41": 0, 
      "sections_42": 0, 
      "sections_43": 0, 
      "sections_44": 0, 
      "sections_45": 0, 
      "sections_46": 0, 
      "sections_47": 0, 
      "sections_48": 0, 
      "sections_49": 0, 
      "sections_50": 0, 
      "sections_51": 0, 
      "sections_52": -42048, 
      "sections_53": 0, 
      "sections_54": 0, 
      "sections_55": 0, 
      "sections_56": 0, 
      "sections_57": 0, 
      "sections_58": 0, 
      "sections_59": 0, 
      "sections_60": 0, 
      "sections_61": 0, 
      "sections_62": 0, 
      "sections_63": 0, 
      "sections_64": 0, 
      "sections_65": 0, 
      "sections_66": 0, 
      "sections_67": 0, 
      "sections_68": 0, 
      "sections_69": 0, 
      "sections_70": 0, 
      "sections_71": 0, 
      "sections_72": 0, 
      "sections_73": 0, 
      "sections_74": 0, 
      "sections_75": 0, 
      "sections_76": 0, 
      "sections_77": 0, 
      "sections_78": 0, 
      "sections_79": 0, 
      "sections_80": 0, 
      "sections_81": 0, 
      "sections_82": 0, 
      "sections_83": 0, 
      "sections_84": 0, 
      "sections_85": 0, 
      "sections_86": 0, 
      "sections_87": 0, 
      "sections_88": 0, 
      "sections_89": 0, 
      "sections_90": 0, 
      "sections_91": 0, 
      "sections_92": 0, 
      "sections_93": 0, 
      "sections_94": 0, 
      "sections_95": 0, 
      "sections_96": 0, 
      "sections_97": 0, 
      "sections_98": 0, 
      "sections_99": 0, 
      "sections_100": 0, 
      "sections_101": 0, 
      "sections_102": -11.691457748413086, 
      "sections_103": 0, 
      "sections_104": 0, 
      "sections_105": 0, 
      "sections_106": 0, 
      "sections_107": 0, 
      "sections_108": 0, 
      "sections_109": 0, 
      "sections_110": 0, 
      "sections_111": 0, 
      "sections_112": 0, 
      "sections_113": 0, 
      "sections_114": 0, 
      "sections_115": 0, 
      "sections_116": 0, 
      "sections_117": 0, 
      "sections_118": 0, 
      "sections_119": 0, 
      "sections_120": 0, 
      "sections_121": 0, 
      "sections_122": 0, 
      "sections_123": 0, 
      "sections_124": 0, 
      "sections_125": 0, 
      "sections_126": 0, 
      "sections_127": 0, 
      "sections_128": 0, 
      "sections_129": 0, 
      "sections_130": 0, 
      "sections_131": 0, 
      "sections_132": 0, 
      "sections_133": 0, 
      "sections_134": 0, 
      "sections_135": 0, 
      "sections_136": 0, 
      "sections_137": 0, 
      "sections_138": 0, 
      "sections_139": 0, 
      "sections_140": 0, 
      "sections_141": 0, 
      "sections_142": 0, 
      "sections_143": 0, 
      "sections_144": 0, 
      "sections_145": 0, 
      "sections_146": 0, 
      "sections_147": 0, 
      "sections_148": 0, 
      "sections_149": 0, 
      "sections_150": 0, 
      "sections_151": 0, 
      "sections_152": -102464, 
      "sections_153": 0, 
      "sections_154": 0, 
      "sections_155": 0, 
      "sections_156": 0, 
      "sections_157": 2, 
      "sections_158": 0, 
      "sections_159": 0, 
      "sections_160": 0, 
      "sections_161": 0, 
      "sections_162": 0, 
      "sections_163": 0, 
      "sections_164": 2, 
      "sections_165": 0, 
      "sections_166": 0, 
      "sections_167": 2, 
      "sections_168": 0, 
      "sections_169": 0, 
      "sections_170": 0, 
      "sections_171": 0, 
      "sections_172": 0, 
      "sections_173": 0, 
      "sections_174": 0, 
      "sections_175": 0, 
      "sections_176": 0, 
      "sections_177": 0, 
      "sections_178": 0, 
      "sections_179": 0, 
      "sections_180": 0, 
      "sections_181": 2, 
      "sections_182": 0, 
      "sections_183": 0, 
      "sections_184": 0, 
      "sections_185": 0, 
      "sections_186": 0, 
      "sections_187": 0, 
      "sections_188": 0, 
      "sections_189": 0, 
      "sections_190": 0, 
      "sections_191": 0, 
      "sections_192": 0, 
      "sections_193": 0, 
      "sections_194": 0, 
      "sections_195": 0, 
      "sections_196": 0, 
      "sections_197": 0, 
      "sections_198": 0, 
      "sections_199": 0, 
      "sections_200": 0, 
      "sections_201": 0, 
      "sections_202": 0, 
      "sections_203": 0, 
      "sections_204": 0, 
      "sections_205": 2, 
      "sections_206": 0, 
      "sections_207": 0, 
      "sections_208": 0, 
      "sections_209": 0, 
      "sections_210": 0, 
      "sections_211": 0, 
      "sections_212": 0, 
      "sections_213": 0, 
      "sections_214": 0, 
      "sections_215": 0, 
      "sections_216": 0, 
      "sections_217": 0, 
      "sections_218": -1, 
      "sections_219": 0, 
      "sections_220": 0, 
      "sections_221": 0, 
      "sections_222": 0, 
      "sections_223": 0, 
      "sections_224": 0, 
      "sections_225": 0, 
      "sections_226": 0, 
      "sections_227": 0, 
      "sections_228": 3, 
      "sections_229": 0, 
      "sections_230": 0, 
      "sections_231": 0, 
      "sections_232": 0, 
      "sections_233": 0, 
      "sections_234": 0, 
      "sections_235": 0, 
      "sections_236": 0, 
      "sections_237": 0, 
      "sections_238": 0, 
      "sections_239": 0, 
      "sections_240": 0, 
      "sections_241": 0, 
      "sections_242": 3, 
      "sections_243": 0, 
      "sections_244": 0, 
      "sections_245": 0, 
      "sections_246": 0, 
      "sections_247": 0, 
      "sections_248": 0, 
      "sections_249": 0, 
      "sections_250": 0, 
      "sections_251": 0, 
      "sections_252": -1, 
      "sections_253": 0, 
      "sections_254": 0 
    } 
  } 
] 
url = "YOUR_ELASTICSEARCH_ENDPOINT_URL" 
api_key = "YOUR_API_KEY" 
api_id = "YOUR_API_ID" 
# Inicializa el cliente Elasticsearch 
es = Elasticsearch( 
        url, 
        api_key=(api_id, api_key), 
        use_ssl=True, 
        ca_certs=certifi.where() 
    ) 
# Crea el índice 
es.indices.create(index="ember_ml") 
# Ingesta documentos de forma masiva en Elasticsearch 
try: 
    for success, info in helpers.streaming_bulk(es, documents, chunk_size=2500): 
        if not success: 
            print("A document failed:", info) 
except elasticsearch.ElasticsearchException: 
    print("Failed to insert")

Ten en cuenta que los vectores de características deben aplanarse, es decir, cada característica debe ser un campo separado de un tipo de datos soportado (numérico, booleano, texto, palabra clave o IP) en cada documento, debido a que las analíticas de cuadros de datos no soportan matrices con más de un elemento. Observa también que el campo “appeared” (primera visualización) en el set de datos EMBER se modificó para que coincida con un formato de fecha compatible con Elasticsearch con el objetivo de realizar posteriormente visualizaciones de series de tiempo. 

Para asegurarnos de que todos los datos se ingesten en el formato correcto en Elasticsearch, ejecutamos las búsquedas siguientes en la consola Dev Tools (Management > Dev Tools):

Para obtener el recuento de la cantidad de documentos:

GET ember_ml/_count

Para buscar documentos en el índice y asegurarnos de que tengan el formato correcto:

GET ember_ml/_search

Una vez que hayamos verificado que los datos en Elasticsearch se ven como se esperaba, estamos listos para crear nuestros trabajos de analíticas. Sin embargo, antes de crear los trabajos, debemos definir un patrón de índice para el trabajo. Los patrones de índice le indican a Kibana (y en consecuencia al trabajo) cuáles índices de Elasticsearch contienen los datos con los que quieres trabajar. Creamos el patrón de índice ember_* para que coincida con nuestro índice ember_ml.

Entrenamiento del modelo

Una vez creado el patrón de índice, crearemos dos trabajos de analíticas con los dos subconjuntos de características, como mencionamos antes. Esto se puede hacer a través de la app de Machine Learning en Kibana. Configuraremos nuestro trabajo de la siguiente manera:

  • Job type: seleccionamos “classification” para predecir si un binario dado es malicioso o benigno. El modelo de clasificación subyacente en Machine Learning de Elastic es un tipo de mejora llamada regresión de árboles potenciada, que combina varios modelos débiles en uno compuesto. Usa árboles de decisión para aprender a predecir la probabilidad de que un punto de datos pertenezca a una cierta clase.
  • Dependent variable: “label” en nuestro caso, 1 para malicioso y 0 para benigno.
  • Fields to include: seleccionamos los campos que nos gustaría incluir en el entrenamiento. 
  • Training percentage: se recomienda usar un enfoque iterativo de entrenamiento, especialmente si trabajas con un set de datos grande (es decir, comienza por crear un trabajo de entrenamiento con un porcentaje de entrenamiento más pequeño, evalúa el rendimiento y decide si es necesario aumentar el porcentaje de entrenamiento). Comenzaremos con un porcentaje de entrenamiento del 10 % porque trabajamos con un set de datos considerable (300 000 documentos).
  • Additional information options: dejaremos las opciones predeterminadas como están, pero puedes elegir configurar hiperparámetros para el trabajo de entrenamiento en esta etapa.
  • Job details: asignaremos una ID de trabajo e índice de destino apropiados para el trabajo.
  • Create index pattern: deshabilitaremos esta opción porque crearemos un patrón de índice único que coincida con los índices de destino para ambos trabajos de entrenamiento con el objetivo de visualizar juntos los resultados. 

Crearemos dos trabajos de analíticas siguiendo el proceso antes descrito, uno solo con el histograma de bytes como características (índice de destino: bytes_preds) y uno con todo excepto el histograma de bytes como características (índice de destino: main_preds). El trabajo de analíticas determina los mejores codificados para cada característica, las mejores características de rendimiento y los hiperparámetros óptimos para el modelo. El progreso del trabajo también se puede rastrear en la app de Machine Learning:

Tracking job progress in the Machine Learning appTracking job progress in the Machine Learning app

Rastreo del progreso del trabajo en la app de Machine Learning

Evaluación del modelo

Una vez finalizados los trabajos, podemos ver los resultados de la predicción haciendo clic en el botón View junto a cada trabajo completado. Al hacer clic en View, obtenemos una vista del estilo cuadro de datos sobre los contenidos del índice de destino y la matriz de confusión del modelo. Cada fila en el cuadro de datos (abajo) indica si una muestra se usó en el entrenamiento, el modelo de predicción, la etiqueta, la puntuación y probabilidad de clase.

Dataframe view of the main_preds destination index

Vista de cuadro de datos del índice de destino main_preds

Usamos las matrices de confusión para evaluar y comparar el rendimiento de ambos modelos. Cada fila en esta matriz de confusión representa instancias en la clase real y cada columna representa instancias en la clase de predicción, esto nos da una medición de positivos verdaderos, falsos positivos (fila superior), falsos negativos y negativos verdaderos (fila inferior).

Confusion matrix for model with general, file header and section information, and strings as features

Matriz de confusión para el modelo con la información general, de encabezado de archivo y de sección, y cadenas como características

Confusion matrix for model with byte histogram as features

Matrix de confusión para el modelo con el histograma de bytes como características

Vemos que ambos modelos tienen una precisión muy buena (al menos para la demostración), así que decidimos no realizar otra ronda de entrenamiento o ajuste de hiperparámetros. En la sección siguiente, vemos cómo comparar visualmente los dos modelos en Kibana y decidir cuál desplegar.

Monitoreo del modelo

Una vez que tenemos las predicciones de ambos modelos en sus índices de destino respectivos, crearemos un patrón de índice (*_preds, en este caso) para combinarlos y crear dashboards de monitoreo del modelo en Kibana. En este ejemplo, el dashboard de monitoreo tiene dos objetivos:

  • Comparar el rendimiento entre el modelo con solo el histograma de bytes y el otro modelo; usamos las visualizaciones TSVB para esto.
  • Rastrear diferentes métricas del modelo con mejor rendimiento; usamos las visualizaciones de barras verticales para ver las probabilidades de predicción y los recuentos de muestras benignas y maliciosas, además de TSVB para rastrar las tasas de falsos positivos y falsos negativos.

False negative rate of the two trained models over timeFalse positive rate of the two trained models over time

Tasas de falsos positivos y falsos negativos de los dos modelos entrenados en el tiempo

Al observar las tasas de falsos positivos y falsos negativos de los dos modelos en un período representativo, y observando las matrices de confusión de la sección anterior, concluimos que el modelo entrenado con la información general, de encabezado de archivo y de sección, y cadenas es el que tiene mejor rendimiento. Después graficamos varias métricas que deseamos rastrear de este modelo, suponiendo que es el que deseamos desplegar y monitorear una vez desplegado.

Dashboard de varias métricas de rendimiento del modelo creado en Kibana

En los casos de uso reales, estos dashboards de monitoreo se pueden usar para comparar modelos de candidatos para la producción y, una vez desplegado un modelo, para identificar indicadores de deterioro del modelo (por ejemplo, aumentos repentinos de falsos positivos) en entornos de producción y activar respuestas relevantes (por ejemplo, nuevo entrenamiento del modelo). En la sección siguiente, veremos cómo desplegar el modelo elegido para usarlo en un pipeline de producción de Machine Learning.

Despliegue de nuestro modelo supervisado para enriquecer los datos al momento de la ingesta

Además del entrenamiento y la evaluación del modelo, el Elastic Stack también proporciona una forma para que el usuario use modelos entrenados en pipelines de ingesta. A su vez, esto abre camino para usar modelos de Machine Learning con el objetivo de enriquecer los datos al momento de la ingesta. En esta sección, veremos cómo puedes hacer precisamente esto con el modelo de clasificación de malware que entrenamos antes.

Supón que en este caso tenemos un flujo entrante de datos extraído de binarios que deseamos clasificar como maliciosos o benignos. Ingestaremos estos datos en Elasticsearch a través de un pipeline de ingesta y consultaremos nuestro modelo de clasificación de malware entrenado en un procesador de inferencias. 

Primero, creemos nuestro procesador de inferencias y pipeline de ingesta. La parte más importante del procesador de inferencia es el modelo entrenado y su model_id, que podemos buscar con la siguiente llamada de API REST en la consola de Kibana:

GET _ml/inference

Esto devolverá una lista de modelos entrenados en nuestro cluster y mostrará de cada modelo características como model_id (sobre lo que deberíamos hacer una nota para la inferencia), los campos usados para entrenar el modelo, cuándo se entrenó el modelo, etc.

Sample output from a call to retrieve information about trained models shows the model_id, which is required for configuring inference processors

La salida de muestra de una llamada para recuperar información sobre los modelos entrenados incluye model_id, necesario para configurar los procesadores de inferencia

Si tienes una gran cantidad de modelos entrenados en tu cluster, puede ser útil ejecutar la llamada de API anterior con una búsqueda con asterisco basada en el nombre del trabajo de analíticas de cuadro de datos que se usó para entrenar el modelo. En este caso, los modelos que nos interesan se entrenaron con trabajos llamados ember_*, por lo que podemos ejecutar 

GET _ml/inference/ember_*

para acotar rápidamente nuestros modelos a los deseados. 

Una vez que hayamos anotado el model_id, podemos crear nuestra configuración de pipeline de ingesta. A continuación se muestra la configuración completa. Toma nota del bloque de configuración llamado inference. Hace referencia al modelo que deseamos usar para enriquecer nuestros documentos. También especifica un target_field (que en este caso, configuramos como is_malware, pero se puede configurar según lo que se prefiera), que usaremos como prefijo de los campos de ML que se agregarán cuando el procesador de inferencias procese el documento. 

PUT _ingest/pipeline/malware-classification
{
  "description": "Classifies incoming binaries as malicious or benign",
  "processors": [
    {
      "inference": {
        "model_id": "ember_main-1598557689011",
        "target_field": "is_malware",
        "inference_config": {
          "classification": {
            "num_top_classes": 2
          }
        }
      }
    }
  ]
}

Ahora supongamos que ingestamos documentos con características de binarios y deseamos enriquecer estos datos con predicciones sobre la maliciosidad de cada binario. A continuación hay un documento de muestra abreviado:

{ 
          "appeared" : "2020-04-01 00:00:00", 
          "byte_0" : 0.1622137576341629, 
          "byte_1" : 0.007498478516936302, 
          "byte_2" : 0.003992937505245209, 
          "byte_3" : 0.00546838915720582, 
          "byte_4" : 0.007421959958970547, 
          ... 
          "byte_253" : 0.0019106657709926367, 
          "byte_254" : 0.003551538335159421, 
          "byte_255" : 0.1782810389995575, 
          "strings_0" : 3312.0, 
          "strings_1" : 24.97675132751465, 
          "strings_2" : 82723.0, 
          "strings_3" : 0.07208394259214401, 
          "strings_4" : 8.099319529719651E-4, 
          "strings_5" : 0.005427753087133169, 
           ... 
          "strings_100" : 0.0, 
          "strings_101" : 39.0, 
          "strings_102" : 0.0, 
          "strings_103" : 9.0, 
          "general_info_0" : 1130496.0, 
          "general_info_1" : 1134592.0, 
          "general_info_2" : 1.0, 
          "general_info_3" : 0.0, 
          "general_info_4" : 247.0, 
          "general_info_5" : 1.0, 
          "general_info_6" : 1.0, 
          "general_info_7" : 1.0, 
          "general_info_8" : 1.0, 
          "general_info_9" : 0.0, 
          "file_header_0" : 1.511340288E9, 
          "file_header_1" : 0.0, 
          "file_header_2" : 0.0, 
          "file_header_3" : 0.0, 
          "file_header_4" : 0.0, 
          "file_header_5" : 0.0, 
          "file_header_6" : 1.0, 
          "file_header_7" : 0.0, 
          "file_header_8" : 0.0, 
          "file_header_9" : 0.0, 
           ... 
          "file_header_59" : 262144.0, 
          "file_header_60" : 1024.0, 
          "file_header_61" : 4096.0, 
          "sections_0" : 5.0, 
          "sections_1" : 0.0, 
          "sections_2" : 0.0, 
          "sections_3" : 1.0, 
          "sections_4" : 1.0, 
          "sections_5" : 0.0, 
           ... 
          "sections_253" : 0.0, 
          "sections_254" : 0.0 
]

Podemos ingestar este documento usando una de las API de índice y canalizar por el pipeline malware-classification que creamos antes. A continuación puedes ver una llamada de API de muestra que ingesta este documento en un índice de destino llamado main_preds. Para ahorrar espacio, el documento está abreviado. 

POST main_preds/_doc?pipeline=malware-classification 
{ 
          "appeared" : "2020-04-01 00:00:00", 
          "byte_0" : 0.1622137576341629, 
          "byte_1" : 0.007498478516936302, 
          "byte_2" : 0.003992937505245209, 
          "byte_3" : 0.00546838915720582, 
          "byte_4" : 0.007421959958970547, 
          "byte_5" : 0.0025378242135047913, 
          "byte_6" : 0.002135345945134759, 
          "byte_7" : 0.001892974367365241, 
          "byte_8" : 0.007126075681298971, 
          "byte_9" : 0.001768250367604196, 
          "byte_10" : 0.0055223405789583921, 
          "byte_11" : 0.001283507444895804, 
          "byte_12" : 0.008042919423431158, 
          "byte_13" : 0.001533839968033135, 
          "byte_14" : 0.0010570581071078777, 
          "byte_15" : 0.006860705558210611, 
...

Como resultado, en nuestro índice de destino main_preds, ahora tenemos un documento nuevo enriquecido con las predicciones de nuestro modelo de Machine Learning entrenado. Si vemos el documento (por ejemplo, usando la pestaña Discovery) veremos que según nuestra configuración, el procesador de inferencias agregó las predicciones del modelo de Machine Learning entrenado al documento. En este caso, nuestro documento (que representa un binario desconocido que deseamos clasificar como malicioso o benigno) se asignó como clase 1, lo que indica que nuestro modelo predice que este binario es malicioso. 

A snippet from the ingested document shows enrichment from our trained machine learning model

Un fragmento del documento ingestado muestra enriquecimiento de nuestro modelo de Machine Learning entrenado

A medida que se agregan documentos nuevos con predicciones al índice de destino, los dashboards de Kibana los tomarán automáticamente, lo que brindará información sobre el rendimiento del modelo entrenado con muestras nuevas en el tiempo.

Conclusión

En los entornos de producción, no se termina (o no se debería terminar) con el despliegue del modelo. El pipeline debe tener una forma efectiva de evaluar los modelos antes de llegar al entorno del cliente y monitorearlos de cerca una vez desplegados. Esto ayuda a los equipos de ciencia de datos a prever problemas y tomar las medidas necesarias cuando haya indicios de deterioro del modelo.

En este blog, exploramos por qué el Elastic Stack es una excelente plataforma para gestionar un pipeline de Machine Learning integral como este, dado que tiene grandes capacidades de almacenamiento, entrenamiento del modelo, ajuste integrado y un conjunto exhaustivo de herramientas de visualización en Kibana.