[{"data":1,"prerenderedAt":1133},["ShallowReactive",2],{"lang-switch-post-\u002Fplaylists\u002Fneural-networks\u002Fmlp-resolve-o-xor":3,"post-pt-neural-networks-mlp-resolve-o-xor":4},"\u002Fen\u002Fplaylists\u002Fneural-networks\u002Fmlp-resolve-o-xor",{"id":5,"title":6,"body":7,"cover":1117,"date":1118,"description":1119,"extension":1120,"meta":1121,"navigation":78,"order":99,"path":1122,"playlist":1123,"seo":1124,"status":1125,"stem":1126,"tags":1127,"__hash__":1132},"posts\u002Fpt\u002Fplaylists\u002Fneural-networks\u002Fmlp-resolve-o-xor.md","Duas Retas Escondidas Resolvem o XOR (e Nem Precisam Ser Treinadas)",{"type":8,"value":9,"toc":1106},"minimark",[10,33,38,145,194,208,212,241,382,428,459,472,476,490,501,511,570,598,606,608,618,622,635,644,676,680,734,769,786,799,803,806,809,878,884,888,936,940,958,1024,1039,1049,1064,1075,1087,1102],[11,12,13,14,19,20,24,25,28,29,32],"p",{},"Fecho essa primeira leva grande da playlist exatamente onde ela tinha ficado presa: ",[15,16,18],"a",{"href":17},"\u002Fplaylists\u002Fneural-networks\u002Fxor-o-limite-do-perceptron","o post do XOR"," terminou com um neurônio sozinho batendo a cabeça na parede, e um gancho pra quando o professor publicasse a aula de MLP. Não existe aula ",[21,22,23],"code",{},"04b"," no repositório (ele pulou de ",[21,26,27],{},"04a"," direto pra ",[21,30,31],{},"04c","), mas o resto chegou, e paga a promessa inteira: primeiro um MLP com pesos escondidos escolhidos à mão resolve o XOR, depois a mesma arquitetura troca a ativação escondida, e por fim um terceiro jeito de montar os pesos escondidos aparece, sem treinar nada.",[34,35,37],"h2",{"id":36},"a-arquitetura-duas-perguntas-escondidas-em-vez-de-uma","A arquitetura: duas perguntas escondidas em vez de uma",[39,40,45],"pre",{"className":41,"code":42,"language":43,"meta":44,"style":44},"language-python shiki shiki-themes github-light github-dark","class XORClassifer(BaseEstimator, ClassifierMixin):\n  def __init__(self):\n    self.ihw = np.array([[0.5, -1, 1], [0.5, 1, -1]]).T\n    self.how = np.array([0.5, -1, -1])\n\n  def fit(self, X, y):\n    pass\n\n  def predict(self, X):\n    self.Xb = include_bias(X)\n    self.pH = self.Xb @ self.ihw\n    self.H = np.sign(self.pH)\n    self.Hb = include_bias(self.H)\n    logits = self.Hb @ self.how\n    y_pred = np.sign(logits)\n    return y_pred\n","python","",[21,46,47,55,61,67,73,80,86,92,97,103,109,115,121,127,133,139],{"__ignoreMap":44},[48,49,52],"span",{"class":50,"line":51},"line",1,[48,53,54],{},"class XORClassifer(BaseEstimator, ClassifierMixin):\n",[48,56,58],{"class":50,"line":57},2,[48,59,60],{},"  def __init__(self):\n",[48,62,64],{"class":50,"line":63},3,[48,65,66],{},"    self.ihw = np.array([[0.5, -1, 1], [0.5, 1, -1]]).T\n",[48,68,70],{"class":50,"line":69},4,[48,71,72],{},"    self.how = np.array([0.5, -1, -1])\n",[48,74,76],{"class":50,"line":75},5,[48,77,79],{"emptyLinePlaceholder":78},true,"\n",[48,81,83],{"class":50,"line":82},6,[48,84,85],{},"  def fit(self, X, y):\n",[48,87,89],{"class":50,"line":88},7,[48,90,91],{},"    pass\n",[48,93,95],{"class":50,"line":94},8,[48,96,79],{"emptyLinePlaceholder":78},[48,98,100],{"class":50,"line":99},9,[48,101,102],{},"  def predict(self, X):\n",[48,104,106],{"class":50,"line":105},10,[48,107,108],{},"    self.Xb = include_bias(X)\n",[48,110,112],{"class":50,"line":111},11,[48,113,114],{},"    self.pH = self.Xb @ self.ihw\n",[48,116,118],{"class":50,"line":117},12,[48,119,120],{},"    self.H = np.sign(self.pH)\n",[48,122,124],{"class":50,"line":123},13,[48,125,126],{},"    self.Hb = include_bias(self.H)\n",[48,128,130],{"class":50,"line":129},14,[48,131,132],{},"    logits = self.Hb @ self.how\n",[48,134,136],{"class":50,"line":135},15,[48,137,138],{},"    y_pred = np.sign(logits)\n",[48,140,142],{"class":50,"line":141},16,[48,143,144],{},"    return y_pred\n",[11,146,147,148,151,152,155,156,160,161,164,165,168,169,173,174,177,178,182,183,186,187,168,190,193],{},"Repara no ",[21,149,150],{},"fit",": só tem um ",[21,153,154],{},"pass"," lá dentro. Esse modelo ",[157,158,159],"strong",{},"não treina",", os pesos já vêm prontos no ",[21,162,163],{},"__init__",". ",[21,166,167],{},"ihw"," (",[170,171,172],"em",{},"input-to-hidden weights",") é uma matriz ",[21,175,176],{},"(3,2)",": cada coluna é um neurônio da camada escondida, com seu próprio bias e seus próprios dois pesos, ",[15,179,181],{"href":180},"\u002Fplaylists\u002Fneural-networks\u002Fadaline-regra-delta","a mesma pré-ativação de sempre",", só que agora tem ",[157,184,185],{},"dois"," neurônios calculando isso em paralelo, não um só. ",[21,188,189],{},"how",[170,191,192],{},"hidden-to-output weights",") é o terceiro neurônio, que pega a saída dos dois primeiros como se fossem duas novas variáveis de entrada.",[11,195,196,197,200,201,168,204,207],{},"Essa é a primeira vez, nessa playlist inteira, que um output vira input de outra coisa. É isso que separa um MLP (",[170,198,199],{},"multilayer perceptron",", rede de múltiplas camadas) de tudo que eu vi até aqui: uma ",[157,202,203],{},"camada escondida",[170,205,206],{},"hidden layer",") entre a entrada e a saída, cada neurônio dela enxergando o dado bruto, mas ninguém de fora enxerga o que ela calcula diretamente, só a próxima camada.",[34,209,211],{"id":210},"o-que-cada-neurônio-escondido-está-perguntando","O que cada neurônio escondido está perguntando",[11,213,214,215,168,218,221,222,168,225,228,229,232,233,236,237,240],{},"Os dois neurônios escondidos, escritos como fronteira: ",[21,216,217],{},"0.5 - x0 + x1 = 0",[21,219,220],{},"x1 = x0 - 0.5",") e ",[21,223,224],{},"0.5 + x0 - x1 = 0",[21,226,227],{},"x1 = x0 + 0.5","). Duas retas ",[157,230,231],{},"paralelas"," (mesma inclinação, ",[21,234,235],{},"1","), uma deslocada meia unidade acima da outra. Calculei a pré-ativação (",[21,238,239],{},"pH",") dos dois neurônios escondidos exatamente nos quatro cantos que o XOR usa:",[242,243,244,275],"table",{},[245,246,247],"thead",{},[248,249,250,255,259,265,270],"tr",{},[251,252,254],"th",{"align":253},"left","Canto",[251,256,258],{"align":257},"center","XOR (rótulo)",[251,260,262,264],{"align":261},"right",[21,263,239],{}," neurônio 1",[251,266,267,269],{"align":261},[21,268,239],{}," neurônio 2",[251,271,272],{"align":253},[21,273,274],{},"H = sign(pH)",[276,277,278,307,335,359],"tbody",{},[248,279,280,286,293,298,302],{},[281,282,283],"td",{"align":253},[21,284,285],{},"(0.1, 0.1)",[281,287,288,289,292],{"align":257},"falso (",[21,290,291],{},"-1",")",[281,294,295],{"align":261},[21,296,297],{},"0.5",[281,299,300],{"align":261},[21,301,297],{},[281,303,304],{"align":253},[21,305,306],{},"[1, 1]",[248,308,309,314,320,325,330],{},[281,310,311],{"align":253},[21,312,313],{},"(0.9, 0.1)",[281,315,316,317,292],{"align":257},"verdadeiro (",[21,318,319],{},"+1",[281,321,322],{"align":261},[21,323,324],{},"-0.3",[281,326,327],{"align":261},[21,328,329],{},"1.3",[281,331,332],{"align":253},[21,333,334],{},"[-1, 1]",[248,336,337,342,346,350,354],{},[281,338,339],{"align":253},[21,340,341],{},"(0.1, 0.9)",[281,343,316,344,292],{"align":257},[21,345,319],{},[281,347,348],{"align":261},[21,349,329],{},[281,351,352],{"align":261},[21,353,324],{},[281,355,356],{"align":253},[21,357,358],{},"[1, -1]",[248,360,361,366,370,374,378],{},[281,362,363],{"align":253},[21,364,365],{},"(0.9, 0.9)",[281,367,288,368,292],{"align":257},[21,369,291],{},[281,371,372],{"align":261},[21,373,297],{},[281,375,376],{"align":261},[21,377,297],{},[281,379,380],{"align":253},[21,381,306],{},[11,383,384,385,388,389,391,392,395,396,399,400,403,404,407,408,411,412,415,416,419,420,423,424,427],{},"Repara em algo notável: os dois cantos ",[157,386,387],{},"falsos"," caem exatamente no mesmo ponto escondido, ",[21,390,306],{},". Cada neurônio escondido é, na prática, uma pergunta de sim\u002Fnão (\"estou acima dessa reta?\"), e o XOR vira verdadeiro exatamente quando as duas respostas ",[157,393,394],{},"discordam"," (uma sim, uma não), e falso quando elas ",[157,397,398],{},"concordam"," (as duas sim, nesse caso, já que nenhum canto cai abaixo das duas retas). Isso não é coincidência de dataset, é a definição do XOR reaparecendo um nível acima: a camada de saída só precisa separar \"as duas respostas concordam\" de \"as duas discordam\", e isso, ",[15,401,402],{"href":17},"olha só",", é de novo XOR, só que agora entre ",[157,405,406],{},"duas respostas binárias"," em vez de duas variáveis contínuas. A diferença é que somar duas respostas que discordam sempre dá ",[21,409,410],{},"0",", e somar duas que concordam sempre dá ",[21,413,414],{},"2"," ou ",[21,417,418],{},"-2",": isso ",[157,421,422],{},"é"," linearmente separável (é só separar por ",[21,425,426],{},"soma","), o que a variável contínua original não permitia.",[11,429,430,431,434,435,438,439,441,442,445,446,448,449,452,453,448,455,458],{},"Com ",[21,432,433],{},"how = [0.5, -1, -1]",", a saída é ",[21,436,437],{},"0.5 - H₀ - H₁",", ou seja, ",[21,440,297],{}," menos a soma das duas respostas escondidas. Cantos falsos (",[21,443,444],{},"[1,1]",", soma ",[21,447,414],{},"): ",[21,450,451],{},"0.5 - 2 = -1.5",", negativo. Cantos verdadeiros (soma ",[21,454,410],{},[21,456,457],{},"0.5 - 0 = 0.5",", positivo. Bate exatamente com o rótulo, sem precisar de nenhum treino, porque os pesos já foram escolhidos sabendo disso.",[460,461,462],"blockquote",{},[11,463,464,467,468,471],{},[157,465,466],{},"Saída:"," acurácia ",[157,469,470],{},"1.0",".",[34,473,475],{"id":474},"a-fronteira-final-duas-retas-viram-uma-faixa","A fronteira final: duas retas viram uma faixa",[477,478],"mlp-decision-region-chart",{":classes":479,":hidden-weights":480,":output-weights":481,":points":482,":x-max":483,":x-min":484,":y-max":483,":y-min":484,"activation":485,"negative-label":486,"positive-label":487,"x-label":488,"y-label":489},"[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]","[[0.5,-1,1],[0.5,1,-1]]","[0.5,-1,-1]","[[0.097, 0.094], [0.152, 0.037], [0.137, 0.014], [0.09, 0.088], [0.156, 0.099], [0.069, 0.169], [0.181, 0.066], [0.135, 0.078], [0.108, 0.113], [0.036, 0.103], [0.847, 0.051], [0.877, 0.001], [0.826, 0.112], [0.932, 0.143], [0.877, 0.135], [0.978, 0.159], [0.831, 0.013], [0.92, 0.162], [0.899, 0.148], [0.912, 0.111], [0.115, 0.911], [0.079, 0.992], [0.146, 0.872], [0.086, 0.848], [0.124, 0.829], [0.083, 0.937], [0.128, 0.929], [0.041, 0.938], [0.093, 0.966], [0.086, 0.984], [0.921, 0.937], [0.88, 0.88], [0.881, 0.95], [0.878, 0.894], [0.958, 0.926], [0.887, 0.914], [0.83, 0.825], [0.922, 1.038], [1.001, 1.026], [0.929, 0.874]]","1.5","-0.5","sign","falso (XOR)","verdadeiro (XOR)","x0","x1",[11,491,492,493,496,497,500],{},"A região azul não é mais um semiplano cortado por uma reta, é uma ",[157,494,495],{},"faixa diagonal"," entre as duas retas paralelas dos neurônios escondidos. É exatamente essa forma que um único neurônio ",[15,498,499],{"href":17},"nunca conseguiu desenhar",": uma faixa tem duas fronteiras, uma reta só tem uma. Duas fronteiras lineares, combinadas, produzem uma forma que nenhuma delas sozinha conseguia.",[34,502,504,505,507,508],{"id":503},"aula-4d-trocando-sign-por-tanh","Aula 4d: trocando ",[21,506,485],{}," por ",[21,509,510],{},"tanh",[39,512,514],{"className":41,"code":513,"language":43,"meta":44,"style":44},"class XORClassifer(BaseEstimator, ClassifierMixin):\n  def __init__(self):\n    self.ihw = np.array([[0.5, -1, 1], [0.5, 1, -1]]).T\n    self.how = np.array([0.75, -0.9, -0.9])\n\n  def predict(self, X):\n    self.Xb = include_bias(X)\n    self.pH = self.Xb @ self.ihw\n    self.H = np.tanh(self.pH)\n    self.Hb = include_bias(self.H)\n    logits = self.Hb @ self.how\n    y_pred = np.sign(logits)\n    return y_pred\n",[21,515,516,520,524,528,533,537,541,545,549,554,558,562,566],{"__ignoreMap":44},[48,517,518],{"class":50,"line":51},[48,519,54],{},[48,521,522],{"class":50,"line":57},[48,523,60],{},[48,525,526],{"class":50,"line":63},[48,527,66],{},[48,529,530],{"class":50,"line":69},[48,531,532],{},"    self.how = np.array([0.75, -0.9, -0.9])\n",[48,534,535],{"class":50,"line":75},[48,536,79],{"emptyLinePlaceholder":78},[48,538,539],{"class":50,"line":82},[48,540,102],{},[48,542,543],{"class":50,"line":88},[48,544,108],{},[48,546,547],{"class":50,"line":94},[48,548,114],{},[48,550,551],{"class":50,"line":99},[48,552,553],{},"    self.H = np.tanh(self.pH)\n",[48,555,556],{"class":50,"line":105},[48,557,126],{},[48,559,560],{"class":50,"line":111},[48,561,132],{},[48,563,564],{"class":50,"line":117},[48,565,138],{},[48,567,568],{"class":50,"line":123},[48,569,144],{},[11,571,572,573,575,576,579,580,583,584,586,587,589,590,593,594,597],{},"Mesmo ",[21,574,167],{},", mesmas duas retas escondidas. A única mudança é ",[21,577,578],{},"H = np.tanh(self.pH)"," no lugar de ",[21,581,582],{},"H = np.sign(self.pH)"," (e ",[21,585,189],{}," reajustado, de ",[21,588,481],{}," pra ",[21,591,592],{},"[0.75,-0.9,-0.9]",", pra compensar a escala diferente). ",[15,595,596],{"href":180},"Como já vi no Adaline",", a diferença entre pré-ativação e pós-ativação não é só teórica: aqui ela decide se a fronteira final tem quina ou é suave.",[460,599,600],{},[11,601,602,467,604,471],{},[157,603,466],{},[157,605,470],{},[477,607],{":classes":479,":hidden-weights":480,":output-weights":592,":points":482,":x-max":483,":x-min":484,":y-max":483,":y-min":484,"activation":510,"negative-label":486,"positive-label":487,"x-label":488,"y-label":489},[11,609,610,611,613,614,617],{},"Compara com o gráfico de cima: mesma faixa diagonal no geral, mas agora com as bordas arredondadas em vez de retas cortadas. ",[21,612,510],{}," satura suavemente perto de ",[21,615,616],{},"±1"," em vez de saltar direto pra lá, então a transição entre \"dentro da faixa\" e \"fora da faixa\" também fica suave.",[34,619,621],{"id":620},"o-que-a-camada-escondida-realmente-faz-transforma-o-espaço","O que a camada escondida realmente faz: transforma o espaço",[11,623,624,625,628,629,631,632,634],{},"O notebook plota a mesma nuvem de pontos depois de passar pelos dois neurônios escondidos (",[21,626,627],{},"H",", não mais ",[21,630,488],{},"\u002F",[21,633,489],{},"), e é aí que a mágica fica óbvia:",[636,637],"perceptron-boundary-snapshot",{":bias":638,":classes":479,":points":639,":w":640,":x-max":235,":x-min":641,":y-max":235,":y-min":641,"negative-label":486,"positive-label":487,"x-label":642,"y-label":643},"0.75","[[0.46, 0.464], [0.367, 0.548], [0.36, 0.553], [0.461, 0.463], [0.416, 0.506], [0.537, 0.381], [0.367, 0.548], [0.416, 0.506], [0.466, 0.458], [0.513, 0.408], [-0.288, 0.861], [-0.36, 0.88], [-0.211, 0.838], [-0.282, 0.859], [-0.237, 0.846], [-0.309, 0.867], [-0.308, 0.866], [-0.252, 0.85], [-0.246, 0.849], [-0.292, 0.862], [0.861, -0.288], [0.888, -0.391], [0.841, -0.222], [0.852, -0.256], [0.835, -0.202], [0.875, -0.34], [0.862, -0.292], [0.885, -0.378], [0.879, -0.357], [0.885, -0.378], [0.475, 0.449], [0.462, 0.463], [0.515, 0.406], [0.475, 0.449], [0.436, 0.487], [0.483, 0.441], [0.458, 0.466], [0.548, 0.366], [0.482, 0.442], [0.418, 0.504]]","[-0.9, -0.9]","-0.6","H0 (neurônio escondido 1)","H1 (neurônio escondido 2)",[11,645,646,647,631,649,651,652,655,656,659,660,664,665,667,668,671,672,675],{},"Nesse espaço novo, os dois clusters \"falso\" (que eram cantos opostos em ",[21,648,488],{},[21,650,489],{},") colapsam praticamente ",[157,653,654],{},"no mesmo lugar",", e os dois clusters \"verdadeiro\" se separam em dois grupos bem distintos, um de cada lado. E olha a fronteira: é uma ",[157,657,658],{},"reta",", exatamente ",[15,661,663],{"href":662},"\u002Fplaylists\u002Fneural-networks\u002Fmcculloch-pitts-perceptron","a mesma fronteira linear de sempre",", só que desenhada em cima de ",[21,666,627],{},", não de ",[21,669,670],{},"x",". Essa é a ideia central por trás de toda rede com camada escondida: ela não \"aprende a curvar uma reta\", ela ",[157,673,674],{},"transforma o espaço"," até o problema virar, de novo, linearmente separável, e aí resolve com a ferramenta mais simples que existe.",[34,677,679],{"id":678},"aula-4e-e-se-os-pesos-escondidos-fossem-só-sorteados","Aula 4e: e se os pesos escondidos fossem só sorteados?",[39,681,683],{"className":41,"code":682,"language":43,"meta":44,"style":44},"class ELM(BaseEstimator, ClassifierMixin):\n  def __init__(self, n_hidden=2):\n    self.ihw = np.random.randn(3,n_hidden)\n\n  def fit(self, X, y):\n    self.Xb = include_bias(X)\n    self.pH = self.Xb @ self.ihw\n    self.H = np.tanh(self.pH)\n    self.Hb = include_bias(self.H)\n    self.how = np.linalg.pinv(self.Hb) @ y\n    return self\n",[21,684,685,690,695,700,704,708,712,716,720,724,729],{"__ignoreMap":44},[48,686,687],{"class":50,"line":51},[48,688,689],{},"class ELM(BaseEstimator, ClassifierMixin):\n",[48,691,692],{"class":50,"line":57},[48,693,694],{},"  def __init__(self, n_hidden=2):\n",[48,696,697],{"class":50,"line":63},[48,698,699],{},"    self.ihw = np.random.randn(3,n_hidden)\n",[48,701,702],{"class":50,"line":69},[48,703,79],{"emptyLinePlaceholder":78},[48,705,706],{"class":50,"line":75},[48,707,85],{},[48,709,710],{"class":50,"line":82},[48,711,108],{},[48,713,714],{"class":50,"line":88},[48,715,114],{},[48,717,718],{"class":50,"line":94},[48,719,553],{},[48,721,722],{"class":50,"line":99},[48,723,126],{},[48,725,726],{"class":50,"line":105},[48,727,728],{},"    self.how = np.linalg.pinv(self.Hb) @ y\n",[48,730,731],{"class":50,"line":111},[48,732,733],{},"    return self\n",[11,735,736,737,168,740,743,744,746,747,750,751,754,755,757,758,760,761,750,764,768],{},"Isso é um ",[157,738,739],{},"ELM",[170,741,742],{},"Extreme Learning Machine","). A diferença pro que eu vi até aqui: ",[21,745,167],{}," (pesos da entrada pra camada escondida) é puramente ",[157,748,749],{},"aleatório",", ",[21,752,753],{},"np.random.randn",", e nunca muda, ",[21,756,150],{}," não ajusta ele em nenhum momento. Só ",[21,759,189],{}," (camada de saída) é de fato calculado, e olha como: ",[21,762,763],{},"np.linalg.pinv(self.Hb) @ y",[15,765,767],{"href":766},"\u002Fplaylists\u002Fneural-networks\u002Fregressao-e-equacao-normal","a mesmíssima pseudo-inversa da equação normal, lá do post 3",". O ELM não treina a rede inteira, ele sorteia uma transformação de espaço aleatória e resolve só a última camada, de uma vez, sem gradiente nenhum.",[460,770,771],{},[11,772,773,467,780,782,783,471],{},[157,774,775,776,779],{},"Saída do notebook (",[21,777,778],{},"n_hidden=4","):",[157,781,470],{},", pesos de saída ",[21,784,785],{},"[-216.55, -14.43, -13.51, -6.63, 232.97]",[11,787,788,789,791,792,750,795,798],{},"Reproduzi isso eu mesmo, com uma semente que também bate ",[21,790,470],{},", pra poder plotar a fronteira final (repara nos pesos de saída, também bem grandes, ",[21,793,794],{},"265.96, -158.78, 3.17, 61.15, -167.19",[15,796,797],{"href":766},"o mesmo comportamento da pseudo-inversa que já vi antes",": ela sempre acha uma resposta exata, mesmo que isso signifique pesos enormes):",[477,800],{":classes":479,":hidden-weights":801,":output-weights":802,":points":482,":x-max":483,":x-min":484,":y-max":483,":y-min":484,"activation":510,"negative-label":486,"positive-label":487,"x-label":488,"y-label":489},"[[1.7641,1.8676,-0.1032],[0.4002,-0.9773,0.4106],[0.9787,0.9501,0.144],[2.2409,-0.1514,1.4543]]","[265.9576,-158.7802,3.1749,61.1483,-167.1908]",[11,804,805],{},"Nada parecido com a faixa organizada de cima, é uma forma irregular, quatro neurônios escondidos apontando pra direções aleatórias, cada um cortando o plano de um jeito diferente. Mas separa os quatro cantos do XOR do mesmo jeito, sem eu (nem o professor) ter escolhido uma única direção à mão.",[11,807,808],{},"Isso levanta a pergunta óbvia: quantos neurônios escondidos aleatórios são necessários pra ter uma chance boa de resolver o XOR? Rodei 15 sementes diferentes pra cada tamanho de camada escondida:",[242,810,811,824],{},[245,812,813],{},[248,814,815,818,821],{},[251,816,817],{"align":257},"Neurônios escondidos",[251,819,820],{"align":261},"Acurácia média",[251,822,823],{"align":261},"Rodadas com acerto perfeito",[276,825,826,836,845,856,867],{},[248,827,828,830,833],{},[281,829,235],{"align":257},[281,831,832],{"align":261},"0.522",[281,834,835],{"align":261},"0 de 15",[248,837,838,840,843],{},[281,839,414],{"align":257},[281,841,842],{"align":261},"0.548",[281,844,835],{"align":261},[248,846,847,850,853],{},[281,848,849],{"align":257},"3",[281,851,852],{"align":261},"0.853",[281,854,855],{"align":261},"3 de 15",[248,857,858,861,864],{},[281,859,860],{"align":257},"4",[281,862,863],{"align":261},"0.977",[281,865,866],{"align":261},"10 de 15",[248,868,869,872,875],{},[281,870,871],{"align":257},"8",[281,873,874],{"align":261},"1.000",[281,876,877],{"align":261},"15 de 15",[11,879,880,881,883],{},"Com 1 ou 2 neurônios, o sorteio quase nunca acerta duas direções que, combinadas, separam o XOR, a acurácia média fica perto de chute (",[21,882,297],{},"). A partir de 3 ou 4, a chance de pelo menos duas direções aleatórias caírem \"bem posicionadas\" cresce rápido, e com 8 elas nunca falharam, nas 15 sementes que testei. Quantidade de direções aleatórias compensa a falta de escolha inteligente, ideia central por trás de toda a família de métodos de projeção aleatória.",[34,885,887],{"id":886},"fechando","Fechando",[242,889,890,900],{},[245,891,892],{},[248,893,894,897],{},[251,895,896],{"align":253},"O que eu já sabia",[251,898,899],{"align":253},"O que essa aula assentou",[276,901,902,910,924],{},[248,903,904,907],{},[281,905,906],{"align":253},"Um neurônio sozinho não resolve XOR",[281,908,909],{"align":253},"Duas retas escondidas combinadas resolvem, transformando o espaço até o problema virar linearmente separável de novo",[248,911,912,915],{},[281,913,914],{"align":253},"Pré-ativação vs. pós-ativação já apareceu no Adaline",[281,916,917,918,920,921,923],{"align":253},"A escolha da ativação escondida (",[21,919,485],{}," vs. ",[21,922,510],{},") muda a forma da fronteira final, de faixa com quina pra faixa suave",[248,925,926,929],{},[281,927,928],{"align":253},"Equação normal resolve regressão de uma vez",[281,930,931,932,935],{"align":253},"O ELM aplica a mesma conta numa camada escondida ",[157,933,934],{},"aleatória",": só a última camada precisa de solução fechada, o resto pode ser sorteado",[34,937,939],{"id":938},"aplicação-prática","Aplicação Prática",[11,941,942,943,750,949,952,953,957],{},"Testei o ELM no dataset de duas luas (",[15,944,945,948],{"href":17},[21,946,947],{},"make_moons",", o mesmo do post do XOR",[21,950,951],{},"noise=0.25",", o mesmo já usado ",[15,954,956],{"href":955},"\u002Fplaylists\u002Fneural-networks\u002Fsigmoide-consertada-hinge-perdeu-margem","no post passado","), variando o número de neurônios escondidos, 10 sementes por tamanho, sempre com divisão treino\u002Fteste 70\u002F30:",[242,959,960,969],{},[245,961,962],{},[248,963,964,966],{},[251,965,817],{"align":253},[251,967,968],{"align":261},"Acurácia teste (média de 10 sementes)",[276,970,971,982,990,998,1006,1016],{},[248,972,973,979],{},[281,974,975,976,292],{"align":253},"Perceptron (sem camada escondida, ",[15,977,978],{"href":17},"post do XOR",[281,980,981],{"align":261},"0.867",[248,983,984,987],{},[281,985,986],{"align":253},"ELM, 1 neurônio",[281,988,989],{"align":261},"0.714",[248,991,992,995],{},[281,993,994],{"align":253},"ELM, 5 neurônios",[281,996,997],{"align":261},"0.881",[248,999,1000,1003],{},[281,1001,1002],{"align":253},"ELM, 20 neurônios",[281,1004,1005],{"align":261},"0.927",[248,1007,1008,1011],{},[281,1009,1010],{"align":253},"ELM, 50 neurônios",[281,1012,1013],{"align":261},[157,1014,1015],{},"0.930",[248,1017,1018,1021],{},[281,1019,1020],{"align":253},"ELM, 100 neurônios",[281,1022,1023],{"align":261},"0.843",[11,1025,1026,1027,1030,1031,1034,1035,471],{},"Com só 1 neurônio aleatório, o ELM fica ",[157,1028,1029],{},"pior"," que o perceptron treinado de verdade: uma única direção sorteada, sem ajuste nenhum, raramente é tão boa quanto uma direção otimizada pelo próprio algoritmo. A partir de 5 neurônios o ELM já alcança o perceptron, e com 20 a 50 ultrapassa bastante, várias direções aleatórias combinadas conseguem curvar a fronteira em volta da forma de lua, coisa que nenhuma reta única consegue. Mas com 100 neurônios a acurácia ",[157,1032,1033],{},"cai",": hidden layer grande demais pra só 210 pontos de treino vira espaço fácil demais de encaixar exatamente, a pseudo-inversa acerta o treino \"perfeitamente\" à custa de generalizar pior, o mesmo tipo de estrada de mão dupla entre complexidade e generalização que ",[15,1036,1038],{"href":1037},"\u002Fplaylists\u002Fmachine-learning-specialization\u002Fw2-lab04-feature-engineering","já apareceu na outra playlist",[11,1040,1041,1042,1044,1045,1048],{},"O código que gerou essa tabela é o mesmo ",[21,1043,739],{}," de lá de cima, só que agora dentro de um laço variando ",[21,1046,1047],{},"n_hidden"," e a semente:",[39,1050,1052],{"className":41,"code":1051,"language":43,"meta":44,"style":44},"elm = ELM(n_hidden=50)\nelm.fit(X_train, y_train)\n",[21,1053,1054,1059],{"__ignoreMap":44},[48,1055,1056],{"class":50,"line":51},[48,1057,1058],{},"elm = ELM(n_hidden=50)\n",[48,1060,1061],{"class":50,"line":57},[48,1062,1063],{},"elm.fit(X_train, y_train)\n",[11,1065,1066,1067,1070,1071,1074],{},"Pegando a melhor das 10 sementes pra 50 neurônios (acurácia de teste ",[21,1068,1069],{},"0.956","), plotando a fronteira sobre os ",[157,1072,1073],{},"90 pontos de teste",", nunca vistos no treino:",[477,1076],{":classes":1077,":hidden-weights":1078,":output-weights":1079,":points":1080,":x-max":1081,":x-min":1082,":y-max":1083,":y-min":1084,"activation":510,"negative-label":1085,"positive-label":1086,"x-label":488,"y-label":489},"[1, 1, 1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, 1, -1, -1, 1, 1, -1, 1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, 1]","[[1.764, -0.895, 1.883], [0.4, 0.387, -1.348], [0.979, -0.511, -1.27], [2.241, -1.181, 0.969], [1.868, -0.028, -1.173], [-0.977, 0.428, 1.944], [0.95, 0.067, -0.414], [-0.151, 0.302, -0.747], [-0.103, -0.634, 1.923], [0.411, -0.363, 1.481], [0.144, -0.672, 1.868], [1.454, -0.36, 0.906], [0.761, -0.813, -0.861], [0.122, -1.726, 1.91], [0.444, 0.177, -0.268], [0.334, -0.402, 0.802], [1.494, -1.63, 0.947], [-0.205, 0.463, -0.155], [0.313, -0.907, 0.614], [-0.854, 0.052, 0.922], [-2.553, 0.729, 0.376], [0.654, 0.129, -1.099], [0.864, 1.139, 0.298], [-0.742, -1.235, 1.326], [2.27, 0.402, -0.695], [-1.454, -0.685, -0.15], [0.046, -0.871, -0.435], [-0.187, -0.579, 1.849], [1.533, -0.312, 0.672], [1.469, 0.056, 0.407], [0.155, -1.165, -0.77], [0.378, 0.901, 0.539], [-0.888, 0.466, -0.674], [-1.981, -1.536, 0.032], [-0.348, 1.488, -0.636], [0.156, 1.896, 0.676], [1.23, 1.179, 0.577], [1.202, -0.18, -0.208], [-0.387, -1.071, 0.396], [-0.302, 1.054, -1.093], [-1.049, -0.403, -1.491], [-1.42, 1.222, 0.439], [-1.706, 0.208, 0.167], [1.951, 0.977, 0.635], [-0.51, 0.356, 2.383], [-0.438, 0.707, 0.944], [-1.253, 0.011, -0.913], [0.777, 1.786, 1.117], [-1.614, 0.127, -1.316], [-0.213, 0.402, -0.462]]","[-2152.198, -2.811, -80.853, 3.077, -17.459, -24.08, -0.653, 2029.747, 717.624, 68.737, -22.837, -11.59, -235.063, 11.619, 0.06, -2401.914, 604.646, -17.405, -575.25, 7.077, 91.445, -117.75, 105.179, 3.536, 22.877, 288.569, -179.658, -67.765, -78.265, 132.702, 1055.225, 10.531, 44.293, -236.05, 22.344, 23.606, -1.939, 16.423, -3762.274, -42.702, -47.89, 3.27, 28.381, -3796.323, 32.026, -0.036, 1.681, 490.96, -6.227, -142.406, 947.283]","[[0.459, -0.307], [2.533, 0.563], [0.775, -0.587], [0.313, 0.02], [1.919, -0.722], [-0.467, 0.153], [1.938, 0.643], [-0.059, 0.977], [1.001, -0.314], [1.285, -0.493], [-0.569, 0.954], [-0.516, 0.844], [0.062, 0.484], [0.626, 1.302], [1.613, 0.5], [1.921, 0.04], [-0.883, 0.769], [1.574, 0.382], [1.2, -0.04], [-1.196, 0.011], [0.07, 1.035], [1.268, -0.456], [0.976, 0.071], [-0.776, 0.618], [1.839, -0.179], [1.958, -0.004], [1.424, 0.352], [0.508, -0.064], [0.089, 1.376], [1.53, -0.02], [0.725, -0.242], [0.735, 0.453], [0.764, 0.067], [-1.03, 0.902], [0.265, 1.636], [0.992, -0.205], [-0.404, 0.661], [0.519, -0.287], [1.167, -0.596], [0.576, -0.121], [1.29, -0.046], [-0.235, 1.188], [-1.088, 0.061], [1.0, -0.268], [-0.046, 1.319], [0.093, 0.118], [0.234, -0.23], [0.364, -0.531], [1.369, -0.069], [1.319, -0.777], [0.356, -0.179], [0.542, -1.002], [1.024, -0.46], [1.414, 0.592], [-0.73, 0.946], [0.149, 0.648], [-0.741, -0.41], [0.029, 1.456], [1.178, -0.145], [0.529, 0.794], [2.187, 0.293], [0.955, 0.648], [0.321, -0.131], [1.787, 0.148], [0.508, 0.51], [1.277, 0.558], [1.535, -0.584], [0.924, 0.607], [-0.236, 0.773], [1.57, -0.272], [0.271, -0.032], [0.692, 0.917], [0.399, 0.695], [0.374, -0.947], [2.224, -0.191], [0.046, 0.335], [0.412, 0.782], [-0.265, 0.983], [0.897, 0.68], [0.494, 0.467], [0.625, 0.516], [2.185, 0.273], [0.382, 0.436], [-1.1, 0.543], [2.113, 0.488], [1.479, 0.104], [0.197, -0.404], [1.417, -0.792], [1.153, -0.648], [1.418, -0.025]]","2.6","-1.3","1.7","-1.1","lua 0","lua 1",[11,1088,1089,1090,1092,1093,1095,1096,1098,1099,1101],{},"50 direções aleatórias combinadas conseguem mesmo curvar em volta das duas luas, nada parecido com a reta única do perceptron nem com a faixa organizada de duas retas do XOR: é uma fronteira irregular, cheia de dobras, mas que segue a forma real dos dados de um jeito que nenhuma reta sozinha alcançaria. Comparando com o ",[21,1091,477],{}," das duas retas escondidas lá em cima: linha por linha, é o mesmo componente, a mesma matemática (",[21,1094,510],{}," na pré-ativação escondida, ",[21,1097,485],{}," no logit final), só que com 50 colunas de peso escondido em vez de 2, e essas 50 colunas vieram de ",[21,1100,753],{},", não de contas escolhidas à mão.",[1103,1104,1105],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":44,"searchDepth":57,"depth":57,"links":1107},[1108,1109,1110,1111,1113,1114,1115,1116],{"id":36,"depth":57,"text":37},{"id":210,"depth":57,"text":211},{"id":474,"depth":57,"text":475},{"id":503,"depth":57,"text":1112},"Aula 4d: trocando sign por tanh",{"id":620,"depth":57,"text":621},{"id":678,"depth":57,"text":679},{"id":886,"depth":57,"text":887},{"id":938,"depth":57,"text":939},null,"2026-08-27","Aula 4c, 4d e 4e: o gancho que fechei lá no post do XOR finalmente se paga. Um MLP de verdade resolve o problema, primeiro com pesos escondidos escolhidos na mão, depois com pesos escondidos sorteados (ELM), a camada de saída sempre resolvida pela mesma equação normal do post 3.","md",{},"\u002Fpt\u002Fplaylists\u002Fneural-networks\u002Fmlp-resolve-o-xor","neural-networks",{"title":6,"description":1119},"published","pt\u002Fplaylists\u002Fneural-networks\u002Fmlp-resolve-o-xor",[1128,1129,1130,1131],"mlp","camada-escondida","elm","xor","zSkgwcqKOpfpsBsj51rHS9bXpfQqCwWgIhRzwB0-0a0",1788965098896]