Ver o impacto de um fator multiplicativo em uma variável de entrada e de ruído
- fator multiplicativo afeta coeficientes (ex.: converção de moeda: R$->US$)
- ruído não afeta os coeficientes*
*assumindo que a média do ruído é zero e forçando que o ruído não desnormalize os dados
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import math
from sklearn.linear_model import LogisticRegression
sns.set()
# Função para aplicar o ruído
def aplly_noise(noise_intensity=1):
def func(input):
noise_amplitude = noise_intensity*math.sin(input*math.pi)/4
noise = np.random.uniform(-noise_amplitude,noise_amplitude)
return input+noise
return func
# "intensidade" dos ruídos
noise_levels = {
'noised_a': 0.33333,
'noised_b': 0.66667,
'noised_c': 1,
}
# dataframe com entrada pura, com diferentes níveis de ruído, entrada aleatória e saída esperada
df = pd.DataFrame()
df['inputs'] = np.linspace(0,1,10000)
df['noised_a'] = df['inputs'].map(aplly_noise(noise_levels['noised_a']))
df['noised_b'] = df['inputs'].map(aplly_noise(noise_levels['noised_b']))
df['noised_c'] = df['inputs'].map(aplly_noise(noise_levels['noised_c']))
df['random'] = np.random.uniform(0,1, len(df['inputs']))
df['outputs'] = df['inputs'].map(lambda input: 0 if input<0.5 else 1)
# primeiras e ultimas linhas para ilustrar
df.take([0,1,-2,-1])
| inputs | noised_a | noised_b | noised_c | random | outputs | |
|---|---|---|---|---|---|---|
| 0 | 0.0000 | 0.000000 | 0.000000 | 0.000000 | 0.500649 | 0 |
| 1 | 0.0001 | 0.000086 | 0.000060 | 0.000166 | 0.460489 | 0 |
| 9998 | 0.9999 | 0.999923 | 0.999905 | 0.999829 | 0.813542 | 1 |
| 9999 | 1.0000 | 1.000000 | 1.000000 | 1.000000 | 0.646140 | 1 |
# Dispersão de todas as colunas do dataframe
fig, ax = plt.subplots()
ax.scatter(x=df.inputs, y=df.random, label="random", alpha=.15)
ax.scatter(x=df.inputs, y=df.noised_c, label="noised_c", alpha=.15)
ax.scatter(x=df.inputs, y=df.noised_b, label="noised_b", alpha=.15)
ax.scatter(x=df.inputs, y=df.noised_a, label="noised_a", alpha=.15)
ax.scatter(x=df.inputs, y=df.outputs, label="outputs", alpha=.15)
ax.scatter(x=df.inputs, y=df.inputs, label="inputs", alpha=.15)
ax.legend()
<matplotlib.legend.Legend at 0x224363d52b0>
inputs: entrada ideal para determinar os outputsnoised_X: variações do inputs com algum ruído. Ruído se intensifica apenas no centro para evitar a "desnormalização" dos dados (mesmo com o ruído, tudo fica entre 0 e 1)inputs: entrada aleatória que não deve apresentar correlação com outputsinputs:¶X = df[['inputs']]
y = df['outputs']
clf = LogisticRegression().fit(X, y)
y_pred = clf.predict(X)
y_proba = clf.predict_proba(X)
input_coefs = (clf.coef_[0][0], clf.intercept_[0])
plt.scatter(x=y, y=y_pred, s=100)
<matplotlib.collections.PathCollection at 0x224334d7730>
regressão perfeita, nenhum falso positivo ou falso negativo
noised_a:¶X = df[['noised_a']]
y = df['outputs']
clf = LogisticRegression().fit(X, y)
y_pred = clf.predict(X)
y_proba = clf.predict_proba(X)
noised_a_coefs = (clf.coef_[0][0], clf.intercept_[0])
plt.scatter(x=y, y=y_pred, alpha=0.01, s=100)
<matplotlib.collections.PathCollection at 0x224367371f0>
regressão imperfeita, alguns falsos positivos e falsos negativos
print('input_coefs:',input_coefs)
print('noised_a_coefs:',noised_a_coefs)
input_coefs: (25.430619174690186, -12.71529402057659) noised_a_coefs: (22.227859572733315, -11.097058898179354)
O ruido afetou o coeficiente
X = df[['inputs','noised_a','noised_b','noised_c','random']]
y = df['outputs']
clf = LogisticRegression().fit(X, y)
y_pred = clf.predict(X)
y_proba = clf.predict_proba(X)
original_coefs = pd.DataFrame(clf.coef_, columns=X.columns)
original_coefs['intercept'] = clf.intercept_[0]
original_coefs
| inputs | noised_a | noised_b | noised_c | random | intercept | |
|---|---|---|---|---|---|---|
| 0 | 16.266441 | 10.440724 | 4.744826 | 2.721737 | -0.225271 | -16.965691 |
X = df[['inputs','noised_a','noised_b','noised_c','random']].copy()
X['noised_a'] = X['noised_a'].map(lambda vals: vals*2)
y = df['outputs']
clf = LogisticRegression().fit(X, y)
y_pred = clf.predict(X)
y_proba = clf.predict_proba(X)
coefs = pd.DataFrame(clf.coef_, columns=X.columns)
coefs['intercept'] = clf.intercept_[0]
print("Original:")
print(original_coefs)
print('\n')
print("noised_a multiplicado:")
print(coefs)
Original:
inputs noised_a noised_b noised_c random intercept
0 16.266441 10.440724 4.744826 2.721737 -0.225271 -16.965691
noised_a multiplicado:
inputs noised_a noised_b noised_c random intercept
0 13.831039 9.046608 4.435897 2.679733 -0.282497 -19.365238
Todos os coeficientes vão mudando, o que já complica um pouco e tem o intercept ( = $\beta_0$?) que também muda. Não consegui entender o padrão, o comportamento parece completamente diferente quando o fator multiplicaivo é: 2, 5, 10 e 100
fatores_mult = [1/32, 1/16, 1/8, 1/4, 1/2, 1, 2, 4, 8, 16, 32]
factors_df = pd.DataFrame()
for fator in fatores_mult:
X = df[['inputs','noised_a','noised_b','noised_c','random']].copy()
X['noised_a'] = X['noised_a'].map(lambda vals: vals*fator)
y = df['outputs']
clf = LogisticRegression().fit(X, y)
y_pred = clf.predict(X)
y_proba = clf.predict_proba(X)
coefs = pd.DataFrame(clf.coef_, columns=X.columns)
coefs['intercept'] = clf.intercept_[0]
coefs['fator'] = fator
coefs.set_index('fator',inplace=True)
factors_df = pd.concat([factors_df, coefs], axis=0)
factors_df
| inputs | noised_a | noised_b | noised_c | random | intercept | |
|---|---|---|---|---|---|---|
| fator | ||||||
| 0.03125 | 21.038048 | 0.681724 | 5.516272 | 3.010015 | -0.165315 | -14.705080 |
| 0.06250 | 21.004670 | 1.361994 | 5.509834 | 3.006976 | -0.165700 | -14.715389 |
| 0.12500 | 20.870412 | 2.685928 | 5.485849 | 2.996384 | -0.166883 | -14.755665 |
| 0.25000 | 20.376705 | 5.082119 | 5.399291 | 2.958832 | -0.171314 | -14.911819 |
| 0.50000 | 18.914167 | 8.416017 | 5.151754 | 2.857591 | -0.186550 | -15.466653 |
| 1.00000 | 16.266441 | 10.440724 | 4.744826 | 2.721737 | -0.225271 | -16.965691 |
| 2.00000 | 13.831039 | 9.046608 | 4.435897 | 2.679733 | -0.282497 | -19.365238 |
| 4.00000 | 12.578081 | 5.875131 | 4.317976 | 2.713398 | -0.327946 | -21.373614 |
| 8.00000 | 12.146465 | 3.224738 | 4.290104 | 2.738385 | -0.348273 | -22.293140 |
| 16.00000 | 12.020290 | 1.655972 | 4.280878 | 2.748854 | -0.354477 | -22.575922 |
| 32.00000 | 11.988670 | 0.833784 | 4.279443 | 2.751425 | -0.356220 | -22.652429 |
ax = sns.heatmap(factors_df, annot=True)
ax.set_title('Heatmap coeficientes brutos')
Text(0.5, 1.0, 'Heatmap coeficientes brutos')
ax = sns.heatmap(factors_df.drop(['noised_b','noised_c','random'], axis=1), annot=True)
ax.set_title('Heatmap colunas de interesse (brutas)')
Text(0.5, 1.0, 'Heatmap colunas de interesse (brutas)')
def normalize(max,min):
def func(val):
if max==min:
return val
return (val-min)/(max-min)
return func
factors_df_norm = factors_df.copy()
for col in factors_df_norm:
max = factors_df_norm[col].max()
min = factors_df_norm[col].min()
factors_df_norm[col] = factors_df_norm[col].map(normalize(max,min))
ax = sns.heatmap(factors_df_norm, annot=True)
ax.set_title('normalizando por culunas\n(considerar apenas a proporção entre linhas)')
factors_df_norm
| inputs | noised_a | noised_b | noised_c | random | intercept | |
|---|---|---|---|---|---|---|
| fator | ||||||
| 0.03125 | 1.000000 | 0.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 |
| 0.06250 | 0.996312 | 0.069707 | 0.994795 | 0.990801 | 0.997982 | 0.998703 |
| 0.12500 | 0.981475 | 0.205370 | 0.975402 | 0.958729 | 0.991785 | 0.993635 |
| 0.25000 | 0.926918 | 0.450906 | 0.905418 | 0.845034 | 0.968579 | 0.973986 |
| 0.50000 | 0.765301 | 0.792529 | 0.705280 | 0.538504 | 0.888766 | 0.904173 |
| 1.00000 | 0.472714 | 1.000000 | 0.376271 | 0.127176 | 0.685940 | 0.715552 |
| 2.00000 | 0.203591 | 0.857146 | 0.126496 | 0.000000 | 0.386177 | 0.413621 |
| 4.00000 | 0.065133 | 0.532166 | 0.031154 | 0.101929 | 0.148107 | 0.160911 |
| 8.00000 | 0.017437 | 0.260581 | 0.008619 | 0.177581 | 0.041627 | 0.045209 |
| 16.00000 | 0.003494 | 0.099831 | 0.001160 | 0.209279 | 0.009131 | 0.009627 |
| 32.00000 | 0.000000 | 0.015581 | 0.000000 | 0.217064 | 0.000000 | 0.000000 |
factors_df_norm = factors_df.copy().transpose()
for col in factors_df_norm:
max = factors_df_norm[col].max()
min = factors_df_norm[col].min()
factors_df_norm[col] = factors_df_norm[col].map(normalize(max,min))
factors_df_norm = factors_df_norm.transpose()
ax = sns.heatmap(factors_df_norm, annot=True)
ax.set_title('normalizando por linhas\n(considerar apenas a proporção entre colunas)')
factors_df_norm
| inputs | noised_a | noised_b | noised_c | random | intercept | |
|---|---|---|---|---|---|---|
| fator | ||||||
| 0.03125 | 1.0 | 0.430483 | 0.565741 | 0.495622 | 0.406785 | 0.0 |
| 0.06250 | 1.0 | 0.450094 | 0.566215 | 0.496146 | 0.407325 | 0.0 |
| 0.12500 | 1.0 | 0.489574 | 0.568166 | 0.498288 | 0.409497 | 0.0 |
| 0.25000 | 1.0 | 0.566585 | 0.575573 | 0.506415 | 0.417714 | 0.0 |
| 0.50000 | 1.0 | 0.694651 | 0.599707 | 0.532979 | 0.444437 | 0.0 |
| 1.00000 | 1.0 | 0.824696 | 0.653299 | 0.592421 | 0.503742 | 0.0 |
| 2.00000 | 1.0 | 0.855874 | 0.716982 | 0.664080 | 0.574846 | 0.0 |
| 4.00000 | 1.0 | 0.802574 | 0.756710 | 0.709449 | 0.619871 | 0.0 |
| 8.00000 | 1.0 | 0.740946 | 0.771880 | 0.726824 | 0.637199 | 0.0 |
| 16.00000 | 1.0 | 0.700420 | 0.776293 | 0.732010 | 0.642309 | 0.0 |
| 32.00000 | 1.0 | 0.677987 | 0.777454 | 0.733344 | 0.643635 | 0.0 |
noised_a com fator multiplicativo:¶Spoiler: Não consegui fazer o coeficiente de noised_a superar o de inputs, mesmo multiplicando as entradas por um valor elevado (mas se em vez disso tivesse tentado reduzir o inputs daria certo)
Mas era pra conseguir, não era?
X = df[['inputs','noised_a','noised_b','noised_c','random']].copy()
X['noised_a'] = X['noised_a'].map(lambda vals: vals*10) # Não deu
# X['noised_a'] = X['noised_a'].map(lambda vals: vals/10) # Tbm não deu
# X['inputs'] = X['inputs'].map(lambda vals: vals/100) # Esse deu
y = df['outputs']
clf = LogisticRegression().fit(X, y)
y_pred = clf.predict(X)
y_proba = clf.predict_proba(X)
coefs = pd.DataFrame(clf.coef_, columns=X.columns)
coefs['intercept'] = clf.intercept_[0]
print("Original:")
print(original_coefs)
print('\n')
print("noised_a multiplicado:")
print(coefs)
Original:
inputs noised_a noised_b noised_c random intercept
0 16.266441 10.440724 4.744826 2.721737 -0.225271 -16.965691
noised_a multiplicado:
inputs noised_a noised_b noised_c random intercept
0 12.084609 2.612525 4.284634 2.743888 -0.351192 -22.424245
X = df[['noised_a']]
y = df['outputs']
clf = LogisticRegression().fit(X, y)
y_pred = clf.predict(X)
y_proba = clf.predict_proba(X)
input_coefs = (clf.coef_[0][0], clf.intercept_[0])
X = df[['noised_a']].copy()
X['noised_a'] = X['noised_a']*2
y = df['outputs']
clf = LogisticRegression().fit(X, y)
y_pred = clf.predict(X)
y_proba = clf.predict_proba(X)
input_coefs_2 = (clf.coef_[0][0], clf.intercept_[0])
X = df[['noised_a']].copy()
X['noised_a'] = X['noised_a']/2
y = df['outputs']
clf = LogisticRegression().fit(X, y)
y_pred = clf.predict(X)
y_proba = clf.predict_proba(X)
input_coefs_3 = (clf.coef_[0][0], clf.intercept_[0])
print("Entrada dividida:",input_coefs_3)
print("Com entrada original:",input_coefs)
print("Entrada multiplicada:",input_coefs_2)
Entrada dividida: (30.189564633090004, -7.536122743789212) Com entrada original: (22.227859572733315, -11.097058898179354) Entrada multiplicada: (14.983221061171188, -14.960659337619441)
Havia me esquecido que, além do $\beta_1$, também tinha o $\beta_0$ para ser considerado.
Pra esse caso isolado as coisas parecem fazer mais sentido, a "entrada dividida" acaba tendo coeficiente menor pois uma variação menor na entrada resulta numa alteração maior esperada pro cálculo da saída (eu acho)
fatores_mult = [1/256, 1/128, 1/64, 1/32, 1/16, 1/8, 1/4, 1/2, 1, 2, 4, 8, 16, 32, 64, 128, 256]
factors_df = pd.DataFrame()
for fator in fatores_mult:
X = df[['noised_a']].copy()
X['noised_a'] = X['noised_a']*fator
y = df['outputs']
clf = LogisticRegression().fit(X, y)
input_coefs = (clf.coef_[0][0], clf.intercept_[0])
factors_df = factors_df.append({
'beta_1': input_coefs[0],
'intercept': input_coefs[1],
'fator': fator,
}, ignore_index=True)
factors_df.set_index('fator',inplace=True)
ax = sns.heatmap(factors_df, annot=True)
ax.set_title(r"Heatmap $\beta_1$ e $\beta_0$ (intercept)")
factors_df
| beta_1 | intercept | |
|---|---|---|
| fator | ||
| 0.003906 | 4.875770 | -0.009511 |
| 0.007812 | 9.658323 | -0.037682 |
| 0.015625 | 18.606130 | -0.145184 |
| 0.031250 | 32.502985 | -0.507231 |
| 0.062500 | 44.584203 | -1.391477 |
| 0.125000 | 45.480043 | -2.838578 |
| 0.250000 | 38.667898 | -4.826462 |
| 0.500000 | 30.189565 | -7.536123 |
| 1.000000 | 22.227860 | -11.097059 |
| 2.000000 | 14.983221 | -14.960659 |
| 4.000000 | 8.905712 | -17.785018 |
| 8.000000 | 4.768229 | -19.044848 |
| 16.000000 | 2.433190 | -19.436939 |
| 32.000000 | 1.223129 | -19.541364 |
| 64.000000 | 0.612399 | -19.568011 |
| 128.000000 | 0.306304 | -19.574677 |
| 256.000000 | 0.153165 | -19.576350 |
sigmoid = lambda x: 1/(1 + np.exp(-x))
def regressao_linear(x, b0, b1):
return b0 + b1*x
def plot_sigmoids(factors_df):
x = np.linspace(-.5, 1, 100)
for index, row in factors_df.iterrows():
b0 = row['intercept']
b1 = row['beta_1']
y = sigmoid(regressao_linear(x, b0, b1))
# plt.plot(x, y, label=f"b0={b0:.2f} | b1={b1:.2f} | fator={index}")
plt.plot(x, y, label=f"fator: {index}")
plt.legend()
mask = factors_df.index<2
plot_sigmoids(factors_df[mask])
factors_df[mask]
| beta_1 | intercept | |
|---|---|---|
| fator | ||
| 0.003906 | 4.875770 | -0.009511 |
| 0.007812 | 9.658323 | -0.037682 |
| 0.015625 | 18.606130 | -0.145184 |
| 0.031250 | 32.502985 | -0.507231 |
| 0.062500 | 44.584203 | -1.391477 |
| 0.125000 | 45.480043 | -2.838578 |
| 0.250000 | 38.667898 | -4.826462 |
| 0.500000 | 30.189565 | -7.536123 |
| 1.000000 | 22.227860 | -11.097059 |
coefs = [
[0, 0],
[1, 0],
[2, 0],
[3, 0],
[4, 0],
[5, 0],
]
factors_df = pd.DataFrame(coefs, columns=['beta_1','intercept'], index=range(len(coefs)))
factors_df
def plot_sigmoids(factors_df):
x = np.linspace(-2.5, 2.5, 100)
for _, row in factors_df.iterrows():
b0 = row['intercept']
b1 = row['beta_1']
y = sigmoid(regressao_linear(x, b0, b1))
plt.plot(x, y, label=f"beta_1: {b1}", c='#333377', alpha=1/(b1+1))
plt.legend()
plot_sigmoids(factors_df)
factors_df
| beta_1 | intercept | |
|---|---|---|
| 0 | 0 | 0 |
| 1 | 1 | 0 |
| 2 | 2 | 0 |
| 3 | 3 | 0 |
| 4 | 4 | 0 |
| 5 | 5 | 0 |
coefs = [
[2, 0],
[2, 1],
[2, 2],
[2, 3],
[2, 4],
[2, 5],
]
factors_df = pd.DataFrame(coefs, columns=['beta_1','intercept'], index=range(len(coefs)))
factors_df
def plot_sigmoids(factors_df):
x = np.linspace(-2.5, 2.5, 100)
for _, row in factors_df.iterrows():
b0 = row['intercept']
b1 = row['beta_1']
y = sigmoid(regressao_linear(x, b0, b1))
plt.plot(x, y, label=f"beta_0: {b0}", c='#333377', alpha=1/(b0+1))
plt.legend()
plot_sigmoids(factors_df)
factors_df
| beta_1 | intercept | |
|---|---|---|
| 0 | 2 | 0 |
| 1 | 2 | 1 |
| 2 | 2 | 2 |
| 3 | 2 | 3 |
| 4 | 2 | 4 |
| 5 | 2 | 5 |