mirror of
https://github.com/carlospolop/hacktricks
synced 2024-12-18 17:16:10 +00:00
287 lines
18 KiB
Markdown
287 lines
18 KiB
Markdown
<details>
|
|
|
|
<summary><strong>Aprenda hacking no AWS do zero ao herói com</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
|
|
|
|
Outras formas de apoiar o HackTricks:
|
|
|
|
* Se você quer ver sua **empresa anunciada no HackTricks** ou **baixar o HackTricks em PDF**, confira os [**PLANOS DE ASSINATURA**](https://github.com/sponsors/carlospolop)!
|
|
* Adquira o [**material oficial PEASS & HackTricks**](https://peass.creator-spring.com)
|
|
* Descubra [**A Família PEASS**](https://opensea.io/collection/the-peass-family), nossa coleção exclusiva de [**NFTs**](https://opensea.io/collection/the-peass-family)
|
|
* **Junte-se ao grupo** 💬 [**Discord**](https://discord.gg/hRep4RUj7f) ou ao grupo [**telegram**](https://t.me/peass) ou **siga-me** no **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/carlospolopm)**.**
|
|
* **Compartilhe suas técnicas de hacking enviando PRs para os repositórios do GitHub** [**HackTricks**](https://github.com/carlospolop/hacktricks) e [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud).
|
|
|
|
</details>
|
|
|
|
|
|
# Tipos básicos de dados possíveis
|
|
|
|
Os dados podem ser **contínuos** (**infinitos** valores) ou **categóricos** (nominais) onde a quantidade de valores possíveis é **limitada**.
|
|
|
|
## Tipos Categóricos
|
|
|
|
### Binário
|
|
|
|
Apenas **2 valores possíveis**: 1 ou 0. No caso de um conjunto de dados onde os valores estão em formato de string (por exemplo, "True" e "False"), você atribui números a esses valores com:
|
|
```python
|
|
dataset["column2"] = dataset.column2.map({"T": 1, "F": 0})
|
|
```
|
|
### **Ordinal**
|
|
|
|
Os **valores seguem uma ordem**, como em: 1º lugar, 2º lugar... Se as categorias forem strings (como: "iniciante", "amador", "profissional", "especialista"), você pode mapeá-las para números como vimos no caso binário.
|
|
```python
|
|
column2_mapping = {'starter':0,'amateur':1,'professional':2,'expert':3}
|
|
dataset['column2'] = dataset.column2.map(column2_mapping)
|
|
```
|
|
* Para **colunas alfabéticas** você pode ordená-las mais facilmente:
|
|
```python
|
|
# First get all the uniq values alphabetically sorted
|
|
possible_values_sorted = dataset.column2.sort_values().unique().tolist()
|
|
# Assign each one a value
|
|
possible_values_mapping = {value:idx for idx,value in enumerate(possible_values_sorted)}
|
|
dataset['column2'] = dataset.column2.map(possible_values_mapping)
|
|
```
|
|
### **Cíclico**
|
|
|
|
Parece **como valor ordinal** porque há uma ordem, mas isso não significa que um é maior que o outro. Além disso, a **distância entre eles depende da direção** que você está contando. Exemplo: Os dias da semana, domingo não é "maior" que segunda-feira.
|
|
|
|
* Existem **diferentes maneiras** de codificar características cíclicas, algumas podem funcionar apenas com alguns algoritmos. **Em geral, a codificação dummy pode ser usada**
|
|
```python
|
|
column2_dummies = pd.get_dummies(dataset.column2, drop_first=True)
|
|
dataset_joined = pd.concat([dataset[['column2']], column2_dummies], axis=1)
|
|
```
|
|
### **Datas**
|
|
|
|
Datas são **variáveis contínuas**. Podem ser vistas como **cíclicas** (porque se repetem) **ou** como **variáveis ordinais** (porque um momento é maior que o anterior).
|
|
|
|
* Geralmente, datas são usadas como **índice**
|
|
```python
|
|
# Transform dates to datetime
|
|
dataset["column_date"] = pd.to_datetime(dataset.column_date)
|
|
# Make the date feature the index
|
|
dataset.set_index('column_date', inplace=True)
|
|
print(dataset.head())
|
|
|
|
# Sum usage column per day
|
|
daily_sum = dataset.groupby(df_daily_usage.index.date).agg({'usage':['sum']})
|
|
# Flatten and rename usage column
|
|
daily_sum.columns = daily_sum.columns.get_level_values(0)
|
|
daily_sum.columns = ['daily_usage']
|
|
print(daily_sum.head())
|
|
|
|
# Fill days with 0 usage
|
|
idx = pd.date_range('2020-01-01', '2020-12-31')
|
|
daily_sum.index = pd.DatetimeIndex(daily_sum.index)
|
|
df_filled = daily_sum.reindex(idx, fill_value=0) # Fill missing values
|
|
|
|
|
|
# Get day of the week, Monday=0, Sunday=6, and week days names
|
|
dataset['DoW'] = dataset.transaction_date.dt.dayofweek
|
|
# do the same in a different way
|
|
dataset['weekday'] = dataset.transaction_date.dt.weekday
|
|
# get day names
|
|
dataset['day_name'] = dataset.transaction_date.apply(lambda x: x.day_name())
|
|
```
|
|
### Multi-categoria/nominal
|
|
|
|
**Mais de 2 categorias** sem ordem relacionada. Use `dataset.describe(include='all')` para obter informações sobre as categorias de cada recurso.
|
|
|
|
* Uma **string de referência** é uma **coluna que identifica um exemplo** (como o nome de uma pessoa). Isso pode ser duplicado (porque 2 pessoas podem ter o mesmo nome), mas a maioria será única. Esses dados são **inúteis e devem ser removidos**.
|
|
* Uma **coluna chave** é usada para **vincular dados entre tabelas**. Neste caso, os elementos são únicos. Esses dados são **inúteis e devem ser removidos**.
|
|
|
|
Para **codificar colunas de multi-categoria em números** (para que o algoritmo de ML as entenda), **codificação dummy é usada** (e **não codificação one-hot** porque não **evita multicolinearidade perfeita**).
|
|
|
|
Você pode obter uma **coluna de multi-categoria codificada em one-hot** com `pd.get_dummies(dataset.column1)`. Isso transformará todas as classes em recursos binários, criando assim **uma nova coluna por classe possível** e atribuirá 1 **valor Verdadeiro a uma coluna**, e o restante será falso.
|
|
|
|
Você pode obter uma **coluna de multi-categoria codificada em dummy** com `pd.get_dummies(dataset.column1, drop_first=True)`. Isso transformará todas as classes em recursos binários, criando assim **uma nova coluna por classe possível menos uma**, já que **as últimas 2 colunas serão refletidas como "1" ou "0" na última coluna binária criada**. Isso evitará multicolinearidade perfeita, reduzindo as relações entre colunas.
|
|
|
|
# Colinear/Multicolinearidade
|
|
|
|
Colinear aparece quando **2 recursos estão relacionados um com o outro**. Multicolinearidade aparece quando são mais de 2.
|
|
|
|
Em ML **você quer que seus recursos estejam relacionados com os resultados possíveis, mas você não quer que eles estejam relacionados entre si**. É por isso que a **codificação dummy mistura as duas últimas colunas** disso e **é melhor do que a codificação one-hot**, que não faz isso, criando uma relação clara entre todos os novos recursos da coluna de multi-categoria.
|
|
|
|
VIF é o **Fator de Inflação de Variância** que **mede a multicolinearidade dos recursos**. Um valor **acima de 5 significa que um dos dois ou mais recursos colineares deve ser removido**.
|
|
```python
|
|
from statsmodels.stats.outliers_influence import variance_inflation_factor
|
|
from statsmodels.tools.tools import add_constant
|
|
|
|
#dummies_encoded = pd.get_dummies(dataset.column1, drop_first=True)
|
|
onehot_encoded = pd.get_dummies(dataset.column1)
|
|
X = add_constant(onehot_encoded) # Add previously one-hot encoded data
|
|
print(pd.Series([variance_inflation_factor(X.values,i) for i in range(X.shape[1])], index=X.columns))
|
|
```
|
|
# Desequilíbrio Categórico
|
|
|
|
Isso ocorre quando **não há a mesma quantidade de cada categoria** nos dados de treinamento.
|
|
```python
|
|
# Get statistic of the features
|
|
print(dataset.describe(include='all'))
|
|
# Get an overview of the features
|
|
print(dataset.info())
|
|
# Get imbalance information of the target column
|
|
print(dataset.target_column.value_counts())
|
|
```
|
|
Em um desequilíbrio, sempre há uma **classe ou classes majoritárias** e uma **classe ou classes minoritárias**.
|
|
|
|
Existem 2 maneiras principais de corrigir esse problema:
|
|
|
|
* **Undersampling**: Remoção aleatória de dados da classe majoritária para que ela tenha o mesmo número de amostras que a classe minoritária.
|
|
```python
|
|
from imblearn.under_sampling import RandomUnderSampler
|
|
rus = RandomUserSampler(random_state=1337)
|
|
|
|
X = dataset[['column1', 'column2', 'column3']].copy()
|
|
y = dataset.target_column
|
|
|
|
X_under, y_under = rus.fit_resample(X,y)
|
|
print(y_under.value_counts()) #Confirm data isn't imbalanced anymore
|
|
```
|
|
* **Oversampling**: Gerar mais dados para a classe minoritária até que ela tenha tantas amostras quanto a classe majoritária.
|
|
```python
|
|
from imblearn.under_sampling import RandomOverSampler
|
|
ros = RandomOverSampler(random_state=1337)
|
|
|
|
X = dataset[['column1', 'column2', 'column3']].copy()
|
|
y = dataset.target_column
|
|
|
|
X_over, y_over = ros.fit_resample(X,y)
|
|
print(y_over.value_counts()) #Confirm data isn't imbalanced anymore
|
|
```
|
|
Você pode usar o argumento **`sampling_strategy`** para indicar a **porcentagem** que deseja **subamostrar ou sobreamostrar** (**por padrão é 1 (100%)**, o que significa igualar o número de classes minoritárias com as classes majoritárias)
|
|
|
|
{% hint style="info" %}
|
|
Subamostragem ou Sobreamostragem não são perfeitas se você obter estatísticas (com `.describe()`) dos dados sub/sobreamostrados e compará-los com os originais, você verá **que eles mudaram.** Portanto, sobreamostragem e subamostragem estão modificando os dados de treinamento.
|
|
{% endhint %}
|
|
|
|
## Sobreamostragem com SMOTE
|
|
|
|
**SMOTE** geralmente é uma **maneira mais confiável de sobreamostrar os dados**.
|
|
```python
|
|
from imblearn.over_sampling import SMOTE
|
|
|
|
# Form SMOTE the target_column need to be numeric, map it if necessary
|
|
smote = SMOTE(random_state=1337)
|
|
X_smote, y_smote = smote.fit_resample(dataset[['column1', 'column2', 'column3']], dataset.target_column)
|
|
dataset_smote = pd.DataFrame(X_smote, columns=['column1', 'column2', 'column3'])
|
|
dataset['target_column'] = y_smote
|
|
print(y_smote.value_counts()) #Confirm data isn't imbalanced anymore
|
|
```
|
|
# Categorias Raramente Ocorrentes
|
|
|
|
Imagine um conjunto de dados onde uma das classes alvo **ocorre muito poucas vezes**.
|
|
|
|
Isso é como o desequilíbrio de categoria da seção anterior, mas a categoria raramente ocorrente está ocorrendo ainda menos que a "classe minoritária" naquele caso. Os métodos de **oversampling** e **undersampling** **puros** também podem ser usados aqui, mas geralmente essas técnicas **não fornecerão resultados realmente bons**.
|
|
|
|
## Pesos
|
|
|
|
Em alguns algoritmos é possível **modificar os pesos dos dados alvo** para que alguns deles tenham por padrão mais importância ao gerar o modelo.
|
|
```python
|
|
weights = {0: 10 1:1} #Assign weight 10 to False and 1 to True
|
|
model = LogisticRegression(class_weight=weights)
|
|
```
|
|
Você pode **misturar os pesos com técnicas de sobreamostragem/subamostragem** para tentar melhorar os resultados.
|
|
|
|
## PCA - Análise de Componentes Principais
|
|
|
|
É um método que ajuda a reduzir a dimensionalidade dos dados. Vai **combinar diferentes características** para **reduzir a quantidade** delas, gerando **características mais úteis** (_menos computação é necessária_).
|
|
|
|
As características resultantes não são compreensíveis por humanos, então também **anonimiza os dados**.
|
|
|
|
# Categorias de Rótulos Incongruentes
|
|
|
|
Os dados podem ter erros devido a transformações mal-sucedidas ou simplesmente por erro humano ao escrever os dados.
|
|
|
|
Portanto, você pode encontrar o **mesmo rótulo com erros de ortografia**, diferentes **capitalizações**, **abreviações** como: _BLUE, Blue, b, bule_. Você precisa corrigir esses erros de rótulo dentro dos dados antes de treinar o modelo.
|
|
|
|
Você pode limpar esses problemas tornando tudo minúsculo e mapeando rótulos mal escritos para os corretos.
|
|
|
|
É muito importante verificar que **todos os dados que você possui estão corretamente rotulados**, porque, por exemplo, um erro de ortografia nos dados, ao codificar as classes em dummies, gerará uma nova coluna nas características finais com **consequências ruins para o modelo final**. Este exemplo pode ser detectado facilmente codificando uma coluna em one-hot e verificando os nomes das colunas criadas.
|
|
|
|
# Dados Ausentes
|
|
|
|
Alguns dados do estudo podem estar ausentes.
|
|
|
|
Pode acontecer de alguns dados completamente aleatórios estarem ausentes por algum erro. Esse tipo de dado é **Missing Completely at Random** (**MCAR**).
|
|
|
|
Pode ser que alguns dados aleatórios estejam ausentes, mas há algo que torna mais provável que alguns detalhes específicos estejam ausentes, por exemplo, é mais frequente que homens revelem sua idade, mas não mulheres. Isso é chamado **Missing at Random** (**MAR**).
|
|
|
|
Finalmente, pode haver dados **Missing Not at Random** (**MNAR**). O valor dos dados está diretamente relacionado com a probabilidade de ter os dados. Por exemplo, se você quer medir algo embaraçoso, quanto mais embaraçosa for a pessoa, menos provável é que ela compartilhe isso.
|
|
|
|
As **duas primeiras categorias** de dados ausentes podem ser **ignoráveis**. Mas a **terceira** requer considerar **apenas partes dos dados** que não são impactadas ou tentar **modelar os dados ausentes de alguma forma**.
|
|
|
|
Uma maneira de descobrir sobre dados ausentes é usar a função `.info()`, pois ela indicará o **número de linhas, mas também o número de valores por categoria**. Se alguma categoria tem menos valores do que o número de linhas, então há alguns dados ausentes:
|
|
```bash
|
|
# Get info of the dataset
|
|
dataset.info()
|
|
|
|
# Drop all rows where some value is missing
|
|
dataset.dropna(how='any', axis=0).info()
|
|
```
|
|
Geralmente é recomendado que se um recurso estiver **ausente em mais de 20%** do conjunto de dados, a **coluna deve ser removida:**
|
|
```bash
|
|
# Remove column
|
|
dataset.drop('Column_name', axis='columns', inplace=True)
|
|
dataset.info()
|
|
```
|
|
{% hint style="info" %}
|
|
Observe que **nem todos os valores ausentes estão faltando no conjunto de dados**. É possível que valores ausentes tenham sido atribuídos o valor "Desconhecido", "n/a", "", -1, 0... Você precisa verificar o conjunto de dados (usando `dataset.column`_`name.value`_`counts(dropna=False)` para verificar os possíveis valores).
|
|
{% endhint %}
|
|
|
|
Se alguns dados estão ausentes no conjunto de dados (e não são muitos), você precisa encontrar a **categoria dos dados ausentes**. Para isso, basicamente precisa saber se os **dados ausentes são aleatórios ou não**, e para isso você precisa descobrir se os **dados ausentes estavam correlacionados com outros dados** do conjunto de dados.
|
|
|
|
Para descobrir se um valor ausente está correlacionado com outra coluna, você pode criar uma nova coluna que coloque 1s e 0s se o dado está ausente ou não e então calcular a correlação entre eles:
|
|
```bash
|
|
# The closer it's to 1 or -1 the more correlated the data is
|
|
# Note that columns are always perfectly correlated with themselves.
|
|
dataset[['column_name', 'cloumn_missing_data']].corr()
|
|
```
|
|
Se você decidir ignorar os dados ausentes, ainda precisará decidir o que fazer com eles: Você pode **remover as linhas** com dados ausentes (os dados de treino para o modelo serão menores), pode **remover a característica** completamente, ou poderia **modelá-la**.
|
|
|
|
Você deve **verificar a correlação entre a característica ausente com a coluna alvo** para ver quão importante essa característica é para o alvo, se for realmente **pequena** você pode **descartá-la ou preenchê-la**.
|
|
|
|
Para preencher dados **contínuos** ausentes, você poderia usar: a **média**, a **mediana** ou usar um algoritmo de **imputação**. O algoritmo de imputação pode tentar usar outras características para encontrar um valor para a característica ausente:
|
|
```python
|
|
from sklearn.impute import KNNImputer
|
|
|
|
X = dataset[['column1', 'column2', 'column3']]
|
|
y = dataset.column_target
|
|
|
|
# Create the imputer that will fill the data
|
|
imputer = KNNImputer(n_neightbors=2, weights='uniform')
|
|
X_imp = imputer.fit_transform(X)
|
|
|
|
# Check new data
|
|
dataset_imp = pd.DataFrame(X_imp)
|
|
dataset.columns = ['column1', 'column2', 'column3']
|
|
dataset.iloc[10:20] # Get some indexes that contained empty data before
|
|
```
|
|
Para preencher dados categóricos, primeiro você precisa pensar se há algum motivo pelo qual os valores estão ausentes. Se for por **escolha dos usuários** (eles não quiseram fornecer os dados), talvez você possa **criar uma nova categoria** indicando isso. Se for devido a erro humano, você pode **remover as linhas** ou a **característica** (verifique os passos mencionados antes) ou **preenchê-la com a moda, a categoria mais usada** (não recomendado).
|
|
|
|
# Combinando Características
|
|
|
|
Se você encontrar **duas características** que estão **correlacionadas** entre si, geralmente você deve **descartar** uma delas (aquela que é menos correlacionada com o alvo), mas você também pode tentar **combiná-las e criar uma nova característica**.
|
|
```python
|
|
# Create a new feautr combining feature1 and feature2
|
|
dataset['new_feature'] = dataset.column1/dataset.column2
|
|
|
|
# Check correlation with target column
|
|
dataset[['new_feature', 'column1', 'column2', 'target']].corr()['target'][:]
|
|
|
|
# Check for collinearity of the 2 features and the new one
|
|
X = add_constant(dataset[['column1', 'column2', 'target']])
|
|
# Calculate VIF
|
|
pd.Series([variance_inflation_factor(X.values, i) for i in range(X.shape[1])], index=X.columns)
|
|
```
|
|
<details>
|
|
|
|
<summary><strong>Aprenda hacking no AWS do zero ao herói com</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
|
|
|
|
Outras formas de apoiar o HackTricks:
|
|
|
|
* Se você quer ver sua **empresa anunciada no HackTricks** ou **baixar o HackTricks em PDF**, confira os [**PLANOS DE ASSINATURA**](https://github.com/sponsors/carlospolop)!
|
|
* Adquira o [**material oficial PEASS & HackTricks**](https://peass.creator-spring.com)
|
|
* Descubra [**A Família PEASS**](https://opensea.io/collection/the-peass-family), nossa coleção de [**NFTs**](https://opensea.io/collection/the-peass-family) exclusivos
|
|
* **Junte-se ao grupo** 💬 [**Discord**](https://discord.gg/hRep4RUj7f) ou ao grupo [**telegram**](https://t.me/peass) ou **siga-me** no **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/carlospolopm)**.**
|
|
* **Compartilhe suas técnicas de hacking enviando PRs para os repositórios github do** [**HackTricks**](https://github.com/carlospolop/hacktricks) e [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud).
|
|
|
|
</details>
|