mirror of
https://github.com/carlospolop/hacktricks
synced 2024-12-22 11:03:24 +00:00
288 lines
18 KiB
Markdown
288 lines
18 KiB
Markdown
|
<details>
|
||
|
|
||
|
<summary><strong>Aprenda hacking 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 maneiras de apoiar o HackTricks:
|
||
|
|
||
|
* Se você quiser ver sua **empresa anunciada no HackTricks** ou **baixar o HackTricks em PDF** Confira os [**PLANOS DE ASSINATURA**](https://github.com/sponsors/carlospolop)!
|
||
|
* Adquira o [**swag 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-nos** no **Twitter** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.**
|
||
|
* **Compartilhe seus truques de hacking enviando PRs para os** [**HackTricks**](https://github.com/carlospolop/hacktricks) e [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) repositórios do github.
|
||
|
|
||
|
</details>
|
||
|
|
||
|
|
||
|
# Tipos básicos de dados possíveis
|
||
|
|
||
|
Os dados podem ser **contínuos** (valores **infinitos**) 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 em que os valores estão em formato de string (por exemplo, "Verdadeiro" e "Falso"), 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 são 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 **um valor ordinal** porque há uma ordem, mas isso não significa que um seja 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 tempo é maior que outro anterior).
|
||
|
|
||
|
* Geralmente as 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 múltiplas categorias em números** (para que o algoritmo de ML as entenda), é usada a **codificação dummy** (e **não a codificação one-hot** porque **não evita a multicolinearidade perfeita**).
|
||
|
|
||
|
Você pode obter uma **coluna de múltiplas categorias codificada one-hot** com `pd.get_dummies(dataset.column1)`. Isso transformará todas as classes em recursos binários, criando **uma nova coluna para cada classe possível** e atribuirá 1 **valor verdadeiro a uma coluna**, e o restante será falso.
|
||
|
|
||
|
Você pode obter uma **coluna de múltiplas categorias codificada dummy** com `pd.get_dummies(dataset.column1, drop_first=True)`. Isso transformará todas as classes em recursos binários, criando **uma nova coluna para cada classe possível menos uma**, pois as **últimas 2 colunas refletirão "1" ou "0" na última coluna binária criada**. Isso evitará a multicolinearidade perfeita, reduzindo as relações entre as colunas.
|
||
|
|
||
|
# Colinear/Multicolinearidade
|
||
|
|
||
|
Colinear aparece quando **2 recursos estão relacionados entre si**. Multicolinearidade aparece quando há mais de 2.
|
||
|
|
||
|
No ML, **você deseja que seus recursos estejam relacionados com os resultados possíveis, mas não deseja que estejam relacionados entre si**. Por isso, 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 múltiplas categorias.
|
||
|
|
||
|
VIF é o **Fator de Inflação da 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 resolver esse problema:
|
||
|
|
||
|
* **Undersampling**: Remover dados selecionados aleatoriamente 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
|
||
|
```
|
||
|
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" %}
|
||
|
A subamostragem ou sobreamostragem não são perfeitas, se você obter estatísticas (com `.describe()`) dos dados sobreamostrados/subamostrados e compará-los com os originais, verá **que eles mudaram**. Portanto, a sobreamostragem e subamostragem estão modificando os dados de treinamento.
|
||
|
{% endhint %}
|
||
|
|
||
|
## Sobreamostragem 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 é semelhante ao desequilíbrio de categorias da seção anterior, mas a categoria raramente ocorrente ocorre ainda menos do que a "classe minoritária" nesse caso. Os métodos de **sobreamostragem** e **subamostragem** **brutos** 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)
|
||
|
```
|
||
|
Pode **misturar os pesos com técnicas de oversampling/undersampling** 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** (_é necessária menos computação_).
|
||
|
|
||
|
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 apenas por erro humano ao escrever os dados.
|
||
|
|
||
|
Portanto, você pode encontrar o **mesmo rótulo com erros de digitação**, **diferentes maiúsculas**, **abreviações** como: _AZUL, Azul, a, azul_. Você precisa corrigir esses erros de rótulo nos dados antes de treinar o modelo.
|
||
|
|
||
|
Você pode resolver esses problemas colocando tudo em minúsculas e mapeando rótulos com erros de digitação para os corretos.
|
||
|
|
||
|
É muito importante verificar se **todos os dados que você possui estão corretamente rotulados**, porque, por exemplo, um erro de digitação nos dados, ao codificar as classes, irá gerar uma nova coluna nas características finais com **consequências ruins para o modelo final**. Esse exemplo pode ser detectado facilmente codificando um atributo e verificando os nomes das colunas criadas.
|
||
|
|
||
|
# Dados Ausentes
|
||
|
|
||
|
Alguns dados do estudo podem estar faltando.
|
||
|
|
||
|
Pode acontecer que alguns dados completos estejam faltando por algum erro. Esse tipo de dado é **Completamente Ausente de Forma Aleatória** (**MCAR**).
|
||
|
|
||
|
Pode ser que alguns dados aleatórios estejam faltando, mas algo está tornando alguns detalhes específicos mais prováveis de estar faltando, por exemplo, os homens frequentemente informam sua idade, mas não as mulheres. Isso é chamado de **Ausente de Forma Aleatória** (**MAR**).
|
||
|
|
||
|
Por fim, pode haver dados **Não Ausentes de Forma Aleatória** (**MNAR**). O valor dos dados está diretamente relacionado com a probabilidade de ter os dados. Por exemplo, se você quiser medir algo embaraçoso, quanto mais embaraçoso alguém for, menos provável é que ele vá compartilhar.
|
||
|
|
||
|
As **duas primeiras categorias** de dados ausentes podem ser **ignoradas**. Mas a **terceira** requer considerar **apenas porções 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 indicará o **número de linhas, mas também o número de valores por categoria**. Se alguma categoria tiver menos valores do que o número de linhas, então há dados faltando:
|
||
|
```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 uma característica está **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" %}
|
||
|
Note que **nem todos os valores ausentes estão faltando no conjunto de dados**. É possível que os valores ausentes tenham sido atribuídos como "Desconhecido", "n/a", "", -1, 0... Você precisa verificar o conjunto de dados (usando `conjunto_de_dados.nome_da_coluna.valor_contagens(dropna=False)` para verificar os possíveis valores).
|
||
|
{% endhint %}
|
||
|
|
||
|
Se alguns dados estiverem faltando no conjunto de dados (e não forem muitos), você precisa encontrar a **categoria dos dados ausentes**. Para isso, basicamente você precisa saber se os **dados ausentes estão em aleatório 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 coloca 1s e 0s se os dados estão faltando 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 decidir ignorar os dados em falta, ainda precisa fazer o que com eles: Você pode **remover as linhas** com dados em falta (os dados de treino para o modelo serão menores), pode **remover completamente a feature**, ou pode **modelá-la**.
|
||
|
|
||
|
Você deve **verificar a correlação entre a feature em falta com a coluna alvo** para ver o quão importante essa feature é para o alvo, se for realmente **pequena**, você pode **descartá-la ou preenchê-la**.
|
||
|
|
||
|
Para preencher dados contínuos em falta, você pode usar: a **média**, a **mediana** ou usar um **algoritmo de imputação**. O algoritmo de imputação pode tentar usar outras features para encontrar um valor para a feature em falta:
|
||
|
```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á alguma razão pela qual os valores estão faltando. 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 por erro humano, você pode **remover as linhas** ou a **característica** (verifique os passos mencionados anteriormente) ou **preenchê-la com a moda, a categoria mais utilizada** (não recomendado).
|
||
|
|
||
|
# Combinando Características
|
||
|
|
||
|
Se você encontrar **duas características** que estão **correlacionadas** entre si, geralmente você deve **descartar** uma delas (a menos correlacionada com o alvo), mas 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 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 maneiras de apoiar o HackTricks:
|
||
|
|
||
|
* Se você deseja ver sua **empresa anunciada no HackTricks** ou **baixar o HackTricks em PDF** Confira os [**PLANOS DE ASSINATURA**](https://github.com/sponsors/carlospolop)!
|
||
|
* Adquira o [**swag 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-nos** no **Twitter** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.**
|
||
|
* **Compartilhe seus truques de hacking enviando PRs para os** [**HackTricks**](https://github.com/carlospolop/hacktricks) e [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) repositórios do github.
|
||
|
|
||
|
</details>
|