Engenharia

Treinar, avaliar, monitorar, inferir: machine learning de ponta a ponta na Elastic

Os pipelines de machine learning evoluíram muito nos últimos anos. Com uma ampla variedade de ferramentas e frameworks para simplificar a criação, o treinamento e a implantação, o tempo de resposta no desenvolvimento do modelo de machine learning melhorou drasticamente. No entanto, mesmo com todas essas simplificações, ainda há uma curva de aprendizado acentuada associada a muitas dessas ferramentas. Mas não com a Elastic.

Para usar o machine learning no Elastic Stack, tudo o que você realmente precisa é que seus dados sejam armazenados no Elasticsearch. Uma vez lá, a extração de insights valiosos dos seus dados é muito simples, bastando que você clique em alguns botões no Kibana. O machine learning é incorporado ao Elastic Stack, permitindo que você crie um pipeline de machine learning de ponta a ponta totalmente operacional de forma fácil e intuitiva. Neste post do blog, faremos exatamente isso.

Por que a Elastic?

Por ser uma empresa de busca, a Elastic foi criada para lidar com grandes quantidades de dados de forma eficiente. A busca e a agregação de dados para análise tornam-se simples e intuitivas com a DSL de consulta do Elasticsearch. Grandes conjuntos de dados podem ser visualizados de várias maneiras no Kibana. A interface de machine learning da Elastic permite fácil seleção de recursos e modelos, treinamento de modelos e ajuste de hiperparâmetros. Depois que você treina e ajusta o seu modelo, o Kibana também pode ser usado para avaliar e monitorar os modelos visualmente. Isso torna o Elastic Stack uma solução completa e integrada para machine learning em nível de produção.

Conjunto de dados de exemplo: EMBER 2018

Vamos demonstrar o machine learning de ponta a ponta no Elastic Stack usando o conjunto de dados EMBER, lançado pela Endgame para permitir a detecção de malware usando recursos estáticos derivados de arquivos executáveis portáteis (PE). Para o propósito desta demonstração, usaremos o conjunto de dados EMBER (Endgame Malware BEnchmark for Research) 2018, que é uma coleção open source de um milhão de amostras. Cada amostra inclui o hash sha256 do arquivo de amostra, o mês em que o arquivo foi visto pela primeira vez, um rótulo e os recursos derivados do arquivo. 

Para este experimento, selecionaremos 300 mil amostras (150 mil maliciosas e 150 mil benignas) do conjunto de dados EMBER 2018. Para realizar o aprendizado supervisionado nas amostras, devemos primeiro selecionar alguns recursos. Os recursos no conjunto de dados são estáticos, derivados do conteúdo de arquivos binários. Decidimos fazer o experimento com as informações gerais, de cabeçalho e de seção do arquivo, strings e histogramas de bytes para estudar o desempenho do modelo no uso de diferentes subconjuntos de recursos do conjunto de dados EMBER. 

Machine learning de ponta a ponta no Elastic Stack: um passo a passo

Para os fins desta demonstração, usaremos o Python Elasticsearch Client para inserir dados no Elasticsearch, o recurso de analítica da estrutura de dados do machine learning da Elastic para criar trabalhos de treinamento e o Kibana para monitorar visualmente os modelos após o treinamento. 

Criaremos dois trabalhos supervisionados: um usando informações gerais, de cabeçalho e de seção do arquivo e strings, e outro usando apenas histogramas de bytes como recursos. Faremos isso para demonstrar o treinamento simultâneo de vários modelos no Elastic Stack e a visualização de vários modelos candidatos posteriormente.

Instalação do Elasticsearch

Para usar o machine learning no Elastic Stack, primeiro precisamos ativar o Elasticsearch com um nó de machine learning. Para isso, podemos iniciar uma avaliação gratuita de 14 dias do Elastic Cloud, que está disponível para qualquer pessoa experimentar. Nossa implantação de exemplo tem as seguintes configurações:

  • Cloud Platform (Plataforma de nuvem): Amazon Web Services
  • Region (Região): US West (norte da Califórnia)
  • Optimization (Otimização): otimizada para E/S
  • Customize Deployment (Customizar a implantação): habilitar machine learning

Também precisamos criar chaves de API e atribuir a elas os privilégios apropriados para interagir com o Elasticsearch usando o Python Elasticsearch Client. Para o nosso passo a passo, vamos inserir dados no índice ember_ml; portanto, criaremos uma chave da seguinte maneira:

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

Ingestão de dados

Assim que tivermos a nossa instância do Elasticsearch configurada, começaremos ingerindo dados em um índice do Elasticsearch. Primeiro, criaremos um índice chamado ember_ml e, em seguida, faremos nele a ingestão dos documentos que compõem o nosso conjunto de dados usando o Python Elasticsearch Client. Faremos a ingestão de todos os recursos necessários para ambos os modelos em um único índice, usando o Streaming Bulk Helper para a ingestão de documentos em massa no Elasticsearch. O código Python para criar o índice ember_ml e fazer a ingestão de documentos em massa nele é o seguinte:

import elasticsearch 
import certifi 
from elasticsearch import Elasticsearch, helpers 
# Longa lista de documentos a serem inseridos no Elasticsearch, mostrando um como exemplo 
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" 
# Inicializar o cliente do Elasticsearch 
es = Elasticsearch( 
        url, 
        api_key=(api_id, api_key), 
        use_ssl=True, 
        ca_certs=certifi.where() 
    ) 
# Criar índice 
es.indices.create(index="ember_ml") 
# Fazer a ingestão de documentos em massa no 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")

Observe que os vetores de recursos precisam ser nivelados, ou seja, cada recurso precisa ser um campo separado de um tipo de dados compatível (numérico, booliano, texto, palavra-chave ou IP) em cada documento, já que a analítica da estrutura de dados não dá suporte para matrizes com mais de um elemento. Observe também que o campo “appeared” (visto pela primeira vez) no conjunto de dados EMBER foi alterado para corresponder a um formato de data compatível com o Elasticsearch, com o objetivo de fazer visualizações de série temporal posteriormente. 

Para garantir que todos os nossos dados sejam ingeridos no formato correto no Elasticsearch, executamos as seguintes consultas no console Dev Tools (Management (Gerenciamento) -> Dev Tools (Ferramentas de gerenciamento)):

Para obter a contagem do número de documentos:

GET ember_ml/_count

Para buscar documentos no índice e verificar se eles estão no formato correto:

GET ember_ml/_search

Depois de verificarmos que os dados no Elasticsearch parecem estar conforme o esperado, estamos prontos para criar nossos trabalhos de analítica. No entanto, antes de criar os trabalhos, precisamos definir um padrão de indexação para o trabalho. Os padrões de indexação dizem ao Kibana (e, consequentemente, ao trabalho) quais índices do Elasticsearch contêm os dados com os quais você quer trabalhar. Criamos o padrão de indexação ember_* para corresponder ao nosso índice ember_ml.

Treinamento de modelo

Assim que o padrão de indexação for criado, criaremos dois trabalhos de analítica com os dois subconjuntos de recursos, conforme mencionado acima. Isso pode ser feito por meio do app Machine Learning no Kibana. Vamos configurar o nosso trabalho da seguinte maneira:

  • Tipo de trabalho: selecionamos “classification” (classificação) para prever se um determinado binário é malicioso ou benigno. O modelo de classificação subjacente no machine learning da Elastic é um tipo de impulsionamento chamado de regressão de árvore impulsionada, que combina vários modelos fracos em um modelo composto. Ele usa árvores de decisão para aprender a prever a probabilidade de um ponto de dados pertencer a uma determinada classe.
  • Variável dependente: “label” (rótulo) no nosso caso, sendo 1 para malicioso e 0 para benigno.
  • Campos a serem incluídos: selecionamos os campos que gostaríamos de incluir no treinamento. 
  • Porcentagem de treinamento: recomenda-se que você use uma abordagem iterativa para o treinamento, especialmente se estiver trabalhando com um grande conjunto de dados (ou seja, comece criando um trabalho de treinamento com uma porcentagem de treinamento menor, avalie o desempenho e decida se é necessário aumentar a porcentagem). Começaremos com uma porcentagem de treinamento de 10%, pois estamos trabalhando com um conjunto de dados considerável (300 mil documentos).
  • Opções de informações adicionais: deixaremos os padrões como estão, mas você pode optar por definir hiperparâmetros para o trabalho de treinamento neste estágio.
  • Detalhes do trabalho: atribuiremos um ID de trabalho apropriado e um índice de destino para o trabalho.
  • Criar o padrão de indexação: vamos desabilitar isso, já que criaremos um único padrão de indexação para corresponder aos índices de destino de ambos os nossos trabalhos de treinamento, a fim de visualizarmos os resultados juntos. 

Criaremos dois trabalhos de analítica seguindo o processo descrito acima: um apenas com o histograma de bytes como recursos (índice de destino: bytes_preds) e um com tudo, exceto o histograma de bytes como recursos (índice de destino: main_preds). O trabalho de analítica determina as melhores codificações para cada recurso, os recursos de melhor desempenho e os hiperparâmetros ideais para o modelo. O andamento do trabalho também pode ser acompanhado no app Machine Learning:

Acompanhamento do andamento do trabalho no app Machine LearningAcompanhamento do andamento do trabalho no app Machine Learning

Acompanhamento do andamento do trabalho no app Machine Learning

Avaliação do modelo

Assim que os trabalhos forem concluídos, poderemos visualizar os resultados da previsão clicando no botão View (Visualizar) ao lado de cada um dos trabalhos concluídos. Clicando em View (Visualizar), obtemos uma visualização ao estilo da estrutura de dados do conteúdo do índice de destino, bem como da matriz de confusão do modelo. Cada linha na estrutura de dados (mostrada abaixo) indica se uma amostra foi usada no treinamento, a previsão do modelo, o rótulo e a probabilidade e a pontuação da classe:

Visualização da estrutura de dados do índice de destino main_preds

Visualização da estrutura de dados do índice de destino main_preds

Usamos as matrizes de confusão para avaliar e comparar o desempenho de ambos os modelos. Cada linha na matriz de confusão aqui representa instâncias na classe real, e cada coluna representa instâncias na classe prevista, dando assim uma medida dos verdadeiros positivos, falsos positivos (linha superior), falsos negativos e verdadeiros negativos (linha inferior).

Matriz de confusão do modelo com informações gerais, de cabeçalho e de seção do arquivo e strings como recursos

Matriz de confusão do modelo com informações gerais, de cabeçalho e de seção do arquivo e strings como recursos

Matriz de confusão do modelo com histograma de bytes como recursos

Matriz de confusão do modelo com histograma de bytes como recursos

Vemos que ambos os modelos têm uma precisão muito boa (pelo menos para fins de demonstração!), portanto, decidimos não fazer outra rodada de treinamento ou ajuste de hiperparâmetro. Na próxima seção, veremos como comparar visualmente os dois modelos no Kibana e decidir qual modelo implantar.

Monitoramento de modelo

Assim que tivermos as previsões para ambos os modelos em seus respectivos índices de destino, criaremos um padrão de indexação (neste caso, *_preds) para combinar os dois, a fim de criar dashboards de monitoramento dos modelos no Kibana. Para este exemplo, o dashboard de monitoramento tem duas finalidades:

  • Comparar o desempenho do modelo apenas de histograma de bytes com o outro modelo; usamos visualizações do TSVB para isso.
  • Acompanhar diferentes métricas para obter o modelo de melhor desempenho; usamos visualizações de barra vertical para visualizar probabilidades de previsão e contagens de amostras benignas versus maliciosas, bem como o TSVB para acompanhar a taxa de falsos positivos e falsos negativos.

Taxa de falsos negativos dos dois modelos treinados ao longo do tempoTaxa de falsos positivos dos dois modelos treinados ao longo do tempo

Taxa de falsos negativos e taxa de falsos positivos dos dois modelos treinados ao longo do tempo

Observando a taxa de falsos negativos e a taxa de falsos positivos dos dois modelos em um intervalo de tempo significativo e olhando para as matrizes de confusão mostradas na seção anterior, concluímos que o modelo treinado em informações gerais, de cabeçalho e de seção do arquivo e strings é o modelo de melhor desempenho. Em seguida, traçamos várias métricas que gostaríamos de acompanhar para esse modelo, presumindo que esse seja o que queiramos implantar e monitorar após a implantação.

Dashboard de várias métricas de desempenho do modelo criado no Kibana

Em casos de uso do mundo real, tais dashboards de monitoramento podem ser usados para comparar modelos candidatos para produção e, uma vez que um modelo tenha sido implantado, identificar indicadores de decadência do modelo (por exemplo, explosões de falsos positivos) em ambientes de produção e disparar respostas relevantes (por exemplo, treinamento de um novo modelo). Na próxima seção, veremos como implantar nosso modelo escolhido para uso em um pipeline de produção de machine learning.

Implantação do nosso modelo supervisionado para enriquecer os dados no momento da ingestão

Além do treinamento e da avaliação do modelo, o Elastic Stack também fornece uma maneira de um usuário utilizar modelos treinados em pipelines de ingestão. Isso, por sua vez, abre um caminho para usar modelos de machine learning para enriquecer seus dados no momento da ingestão. Nesta seção, veremos como você pode fazer exatamente isso com o modelo de classificação de malware que treinamos acima!

Suponha que, neste caso, tenhamos um fluxo de entrada de dados extraídos de binários que desejamos classificar como maliciosos ou benignos. Faremos a ingestão desses dados no Elasticsearch por meio de um pipeline de ingestão e faremos referência ao nosso modelo de classificação de malware treinado em um processador de inferência. 

Primeiro, vamos criar o nosso processador de inferência e o pipeline de ingestão. A parte mais importante do processador de inferência é o modelo treinado e seu model_id, que podemos consultar com a seguinte chamada de REST API no console do Kibana:

GET _ml/inference

Isso retornará uma lista de modelos treinados em nosso cluster e, para cada modelo, exibirá características como o model_id (que devemos anotar para inferência), os campos usados para treinar o modelo, quando o modelo foi treinado e assim por diante.

A saída de amostra de uma chamada para recuperar informações sobre modelos treinados mostra o model_id, que é necessário para configurar os processadores de inferência

A saída de amostra de uma chamada para recuperar informações sobre modelos treinados mostra o model_id, que é necessário para configurar os processadores de inferência

Se você tem um grande número de modelos treinados no seu cluster, pode ser útil executar a chamada de API acima com uma consulta de curinga com base no nome do trabalho de analítica da estrutura de dados que foi usado para treinar o modelo. Neste caso, os modelos que nos interessam foram treinados com trabalhos chamados ember_*, para que possamos executar 

GET _ml/inference/ember_*

e reduzir rapidamente os nossos modelos aos desejados. 

Após anotarmos o model_id, podemos criar a nossa configuração de pipeline de ingestão. A configuração completa é mostrada abaixo. Tome nota do bloco de configuração intitulado inference. Ele faz referência ao modelo que desejamos usar para enriquecer nossos documentos. Também especifica um target_field (que, neste caso, definimos como is_malware, mas pode ser definido de acordo com a preferência), que será usado para prefixar os campos de ML que serão adicionados quando o documento for processado pelo processador de inferência. 

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
          }
        }
      }
    }
  ]
}

Agora suponha que estejamos ingerindo documentos com recursos de binários e queiramos enriquecer esses dados com previsões da má intenção de cada binário. Um documento de amostra encurtado é mostrado abaixo:

{ 
          "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 ingerir esse documento usando uma das APIs de índice e incluir no pipeline de classificação de malware que criamos acima. Um exemplo de chamada de API que faz a ingestão desse documento em um índice de destino chamado main_preds é mostrado abaixo. Para economizar espaço, o documento foi encurtado. 

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, no nosso índice de destino main_preds, agora temos um novo documento que foi enriquecido com as previsões do nosso modelo de machine learning treinado. Se visualizarmos o documento, por exemplo, usando a guia Discover (Descobrir), veremos que, de acordo com a nossa configuração, o processador de inferência adicionou as previsões do modelo de machine learning treinado ao documento. Neste caso, nosso documento (que representa um binário desconhecido que queremos classificar como malicioso ou benigno) recebeu a atribuição de classe 1, o que indica que o nosso modelo prevê que esse binário seja malicioso. 

Um snippet do documento ingerido mostra o enriquecimento do nosso modelo de machine learning treinado

Um snippet do documento ingerido mostra o enriquecimento do nosso modelo de machine learning treinado

À medida que novos documentos com previsões forem adicionados ao índice de destino, eles serão automaticamente selecionados pelos dashboards do Kibana, fornecendo um insight do desempenho do modelo treinado em novas amostras ao longo do tempo.

Conclusão

Em ambientes de produção, a responsabilidade não para (ou não deve parar!) com a implantação do modelo. O pipeline precisa ter uma maneira de avaliar os modelos com eficácia antes que cheguem ao ambiente do cliente e monitorá-los de perto assim que forem implantados. Isso ajuda as equipes de ciência de dados a prever problemas em andamento e tomar as medidas necessárias quando houver indicadores de deterioração do modelo.

Neste post do blog, vimos que o Elastic Stack é uma ótima plataforma para gerenciar um pipeline de machine learning de ponta a ponta, devido a seus ótimos recursos de armazenamento, treinamento de modelo, ajuste integrado e um conjunto completo de ferramentas de visualização no Kibana.