[{"data":1,"prerenderedAt":926},["ShallowReactive",2],{"lang-switch-post-\u002Fplaylists\u002Fneural-networks\u002Fcamada-escondida-tambem-aprende":3,"post-pt-neural-networks-camada-escondida-tambem-aprende":4},"\u002Fen\u002Fplaylists\u002Fneural-networks\u002Fcamada-escondida-tambem-aprende",{"id":5,"title":6,"body":7,"cover":910,"date":911,"description":912,"extension":913,"meta":914,"navigation":73,"order":101,"path":915,"playlist":916,"seo":917,"status":918,"stem":919,"tags":920,"__hash__":925},"posts\u002Fpt\u002Fplaylists\u002Fneural-networks\u002Fcamada-escondida-tambem-aprende.md","A Camada Escondida Também Aprende (o Pulo do Gato do Backprop)",{"type":8,"value":9,"toc":901},"minimark",[10,24,29,32,171,222,240,251,255,268,454,485,490,503,512,534,573,587,615,624,635,639,650,669,691,695,745,749,772,851,872,882,894,897],[11,12,13,18,19,23],"p",{},[14,15,17],"a",{"href":16},"\u002Fplaylists\u002Fneural-networks\u002Fmlp-resolve-o-xor","O post passado"," fechou com duas formas de montar a camada escondida de um MLP: escolher os pesos na mão, sabendo de antemão que retas iam separar o XOR, ou sortear eles aleatoriamente e confiar que, com neurônios suficientes, alguma combinação ia funcionar (o ELM). Nenhuma das duas ",[20,21,22],"em",{},"aprende"," a camada escondida a partir do erro. Uma é fixa porque alguém já sabia a resposta, a outra é fixa porque nem tenta saber. Essa aula fecha essa lacuna: e se, em vez de escolher ou sortear, a camada escondida também pudesse se ajustar sozinha, olhando pro erro que ela mesma ajudou a causar? Essa pergunta tem nome, e o professor deixou ele escrito no meio do código.",[25,26,28],"h2",{"id":27},"aula-5a-a-última-camada-aprende-com-gradiente-não-com-pseudo-inversa","Aula 5a: a última camada aprende com gradiente, não com pseudo-inversa",[11,30,31],{},"Antes de mexer na camada escondida, vale um passo intermediário: pegar a mesma arquitetura do post passado (duas retas escondidas fixas, escolhidas na mão) e treinar só a camada de saída, só que dessa vez com atualização passo a passo em vez da conta fechada da pseudo-inversa.",[33,34,39],"pre",{"className":35,"code":36,"language":37,"meta":38,"style":38},"language-python shiki shiki-themes github-light github-dark","class LastLayer(BaseEstimator, ClassifierMixin):\n  def __init__(self, max_iter=100, learning_rate=0.01):\n    self.max_iter = max_iter\n    self.learning_rate = learning_rate\n\n  def fit(self, X, y):\n    self.Xb = include_bias(X)\n    self.ihw = np.array([[0.5,-1,1],[0.5,1,-1]]).T\n    self.pH = self.Xb @ self.ihw\n    self.H = np.tanh(self.pH)\n    self.Hb = include_bias(self.H)\n    if len(y.shape) == 1:\n      y = y.reshape(-1,1)\n    self.how = np.random.uniform(-1, 1, size=(self.Hb.shape[1], y.shape[1]))\n    for _ in range(self.max_iter):\n      logits = self.Hb @ self.how\n      y_pred = np.sign(logits)\n      error = y - y_pred\n      delta = self.Hb.T @ error\n      self.how += self.learning_rate * delta\n    return self\n","python","",[40,41,42,50,56,62,68,75,81,87,93,99,105,111,117,123,129,135,141,147,153,159,165],"code",{"__ignoreMap":38},[43,44,47],"span",{"class":45,"line":46},"line",1,[43,48,49],{},"class LastLayer(BaseEstimator, ClassifierMixin):\n",[43,51,53],{"class":45,"line":52},2,[43,54,55],{},"  def __init__(self, max_iter=100, learning_rate=0.01):\n",[43,57,59],{"class":45,"line":58},3,[43,60,61],{},"    self.max_iter = max_iter\n",[43,63,65],{"class":45,"line":64},4,[43,66,67],{},"    self.learning_rate = learning_rate\n",[43,69,71],{"class":45,"line":70},5,[43,72,74],{"emptyLinePlaceholder":73},true,"\n",[43,76,78],{"class":45,"line":77},6,[43,79,80],{},"  def fit(self, X, y):\n",[43,82,84],{"class":45,"line":83},7,[43,85,86],{},"    self.Xb = include_bias(X)\n",[43,88,90],{"class":45,"line":89},8,[43,91,92],{},"    self.ihw = np.array([[0.5,-1,1],[0.5,1,-1]]).T\n",[43,94,96],{"class":45,"line":95},9,[43,97,98],{},"    self.pH = self.Xb @ self.ihw\n",[43,100,102],{"class":45,"line":101},10,[43,103,104],{},"    self.H = np.tanh(self.pH)\n",[43,106,108],{"class":45,"line":107},11,[43,109,110],{},"    self.Hb = include_bias(self.H)\n",[43,112,114],{"class":45,"line":113},12,[43,115,116],{},"    if len(y.shape) == 1:\n",[43,118,120],{"class":45,"line":119},13,[43,121,122],{},"      y = y.reshape(-1,1)\n",[43,124,126],{"class":45,"line":125},14,[43,127,128],{},"    self.how = np.random.uniform(-1, 1, size=(self.Hb.shape[1], y.shape[1]))\n",[43,130,132],{"class":45,"line":131},15,[43,133,134],{},"    for _ in range(self.max_iter):\n",[43,136,138],{"class":45,"line":137},16,[43,139,140],{},"      logits = self.Hb @ self.how\n",[43,142,144],{"class":45,"line":143},17,[43,145,146],{},"      y_pred = np.sign(logits)\n",[43,148,150],{"class":45,"line":149},18,[43,151,152],{},"      error = y - y_pred\n",[43,154,156],{"class":45,"line":155},19,[43,157,158],{},"      delta = self.Hb.T @ error\n",[43,160,162],{"class":45,"line":161},20,[43,163,164],{},"      self.how += self.learning_rate * delta\n",[43,166,168],{"class":45,"line":167},21,[43,169,170],{},"    return self\n",[11,172,173,174,177,178,181,182,185,186,189,190,193,194,197,198,201,202,185,206,209,210,213,214,217,218,221],{},"Repara na primeira metade do ",[40,175,176],{},"fit",": ",[40,179,180],{},"ihw"," continua sendo exatamente ",[40,183,184],{},"[[0.5,-1,1],[0.5,1,-1]]",", ",[14,187,188],{"href":16},"as mesmas duas retas escondidas de sempre",", escolhidas na mão e nunca tocadas dentro do laço. O que muda é a segunda metade: ",[40,191,192],{},"how"," não sai mais de uma pseudo-inversa resolvida numa linha só, ele nasce aleatório (",[40,195,196],{},"np.random.uniform",") e vai sendo corrigido, iteração após iteração, por ",[40,199,200],{},"delta = self.Hb.T @ error",". Se esse formato parece familiar, é porque é ",[14,203,205],{"href":204},"\u002Fplaylists\u002Fneural-networks\u002Fadaline-regra-delta","a mesma regra delta do Adaline",[40,207,208],{},"entrada.T @ erro",", só que a \"entrada\" aqui não é mais ",[40,211,212],{},"x0","\u002F",[40,215,216],{},"x1"," crus, é ",[40,219,220],{},"Hb",", a saída da camada escondida já com bias. O dado que essa conta enxerga já passou pela transformação de espaço; a regra de ajuste em cima dele é a mesma de sempre.",[223,224,225],"blockquote",{},[11,226,227,231,232,235,236,239],{},[228,229,230],"strong",{},"Saída:"," acurácia ",[228,233,234],{},"1.0",", pesos de saída ",[40,237,238],{},"[[0.439], [-0.529], [-0.514]]",".",[11,241,242,243,246,247,250],{},"O resultado não devia surpreender: como ",[14,244,245],{"href":16},"já vi no post passado",", depois de passar pelas duas retas escondidas fixas, o XOR vira linearmente separável em cima de ",[40,248,249],{},"H",". Se um problema é linearmente separável, uma regra delta rodando tempo suficiente encontra uma fronteira que separa ele, pouco importa se a fronteira final foi resolvida de uma vez (pseudo-inversa) ou aos poucos (gradiente). A pergunta interessante não é \"isso funciona?\", é a próxima.",[25,252,254],{"id":253},"aula-5b-e-se-a-camada-escondida-também-pudesse-errar-e-se-corrigir","Aula 5b: e se a camada escondida também pudesse errar e se corrigir?",[11,256,257,258,261,262,264,265,267],{},"O notebook reaproveita a mesma classe ",[40,259,260],{},"LastLayer"," ali de cima como ponto de partida (mesmo resultado, acurácia 1.0, pesos de saída diferentes porque o sorteio inicial de ",[40,263,192],{}," muda a cada rodada), só que agora ela ganha dois métodos novos, separados, chamados de dentro do ",[40,266,176],{},":",[33,269,271],{"className":35,"code":270,"language":37,"meta":38,"style":38},"class SingleHiddenLayer(BaseEstimator, ClassifierMixin):\n  def __init__(self, n_hidden=2, max_iter=10000, learning_rate=0.005):\n    self.max_iter = max_iter\n    self.learning_rate = learning_rate\n    self.n_hidden = n_hidden\n    self.activation = np.tanh\n\n  def forward(self, X):\n    self.Xb = include_bias(X)\n    self.pH = self.Xb @ self.ihw\n    self.H = self.activation(self.pH)\n    self.Hb = include_bias(self.H)\n    logits = self.Hb @ self.how\n    return logits\n\n  def backward(self, y, y_pred):\n    ho_delta = y - y_pred\n    ho_grad = self.Hb.T @ ho_delta\n    ### PULO DO GATO ###\n    tanh_grad = (1 - self.H**2)\n    ih_delta = ho_delta @ self.how[1:].T * tanh_grad\n    ### #### ## #### ###\n    ih_grad = self.Xb.T @ ih_delta\n    self.ihw += self.learning_rate * ih_grad\n    self.how += self.learning_rate * ho_grad\n    return self\n\n  def fit(self, X, y):\n    self.ihw = np.random.uniform(-1, 1, size=(X.shape[1]+1, self.n_hidden))\n    if len(y.shape) == 1:\n      y = y.reshape(-1,1)\n    self.how = np.random.uniform(-1, 1, size=(self.ihw.shape[1]+1, y.shape[1]))\n    for _ in range(self.max_iter):\n      logits = self.forward(X)\n      self.backward(y, logits)\n    return self\n",[40,272,273,278,283,287,291,296,301,305,310,314,318,323,327,332,337,341,346,351,356,361,366,371,377,383,389,395,400,405,410,416,421,426,432,437,443,449],{"__ignoreMap":38},[43,274,275],{"class":45,"line":46},[43,276,277],{},"class SingleHiddenLayer(BaseEstimator, ClassifierMixin):\n",[43,279,280],{"class":45,"line":52},[43,281,282],{},"  def __init__(self, n_hidden=2, max_iter=10000, learning_rate=0.005):\n",[43,284,285],{"class":45,"line":58},[43,286,61],{},[43,288,289],{"class":45,"line":64},[43,290,67],{},[43,292,293],{"class":45,"line":70},[43,294,295],{},"    self.n_hidden = n_hidden\n",[43,297,298],{"class":45,"line":77},[43,299,300],{},"    self.activation = np.tanh\n",[43,302,303],{"class":45,"line":83},[43,304,74],{"emptyLinePlaceholder":73},[43,306,307],{"class":45,"line":89},[43,308,309],{},"  def forward(self, X):\n",[43,311,312],{"class":45,"line":95},[43,313,86],{},[43,315,316],{"class":45,"line":101},[43,317,98],{},[43,319,320],{"class":45,"line":107},[43,321,322],{},"    self.H = self.activation(self.pH)\n",[43,324,325],{"class":45,"line":113},[43,326,110],{},[43,328,329],{"class":45,"line":119},[43,330,331],{},"    logits = self.Hb @ self.how\n",[43,333,334],{"class":45,"line":125},[43,335,336],{},"    return logits\n",[43,338,339],{"class":45,"line":131},[43,340,74],{"emptyLinePlaceholder":73},[43,342,343],{"class":45,"line":137},[43,344,345],{},"  def backward(self, y, y_pred):\n",[43,347,348],{"class":45,"line":143},[43,349,350],{},"    ho_delta = y - y_pred\n",[43,352,353],{"class":45,"line":149},[43,354,355],{},"    ho_grad = self.Hb.T @ ho_delta\n",[43,357,358],{"class":45,"line":155},[43,359,360],{},"    ### PULO DO GATO ###\n",[43,362,363],{"class":45,"line":161},[43,364,365],{},"    tanh_grad = (1 - self.H**2)\n",[43,367,368],{"class":45,"line":167},[43,369,370],{},"    ih_delta = ho_delta @ self.how[1:].T * tanh_grad\n",[43,372,374],{"class":45,"line":373},22,[43,375,376],{},"    ### #### ## #### ###\n",[43,378,380],{"class":45,"line":379},23,[43,381,382],{},"    ih_grad = self.Xb.T @ ih_delta\n",[43,384,386],{"class":45,"line":385},24,[43,387,388],{},"    self.ihw += self.learning_rate * ih_grad\n",[43,390,392],{"class":45,"line":391},25,[43,393,394],{},"    self.how += self.learning_rate * ho_grad\n",[43,396,398],{"class":45,"line":397},26,[43,399,170],{},[43,401,403],{"class":45,"line":402},27,[43,404,74],{"emptyLinePlaceholder":73},[43,406,408],{"class":45,"line":407},28,[43,409,80],{},[43,411,413],{"class":45,"line":412},29,[43,414,415],{},"    self.ihw = np.random.uniform(-1, 1, size=(X.shape[1]+1, self.n_hidden))\n",[43,417,419],{"class":45,"line":418},30,[43,420,116],{},[43,422,424],{"class":45,"line":423},31,[43,425,122],{},[43,427,429],{"class":45,"line":428},32,[43,430,431],{},"    self.how = np.random.uniform(-1, 1, size=(self.ihw.shape[1]+1, y.shape[1]))\n",[43,433,435],{"class":45,"line":434},33,[43,436,134],{},[43,438,440],{"class":45,"line":439},34,[43,441,442],{},"      logits = self.forward(X)\n",[43,444,446],{"class":45,"line":445},35,[43,447,448],{},"      self.backward(y, logits)\n",[43,450,452],{"class":45,"line":451},36,[43,453,170],{},[11,455,456,457,459,460,462,463,465,466,468,469,472,473,476,477,480,481,484],{},"Duas mudanças de uma vez em relação à ",[40,458,260],{},". Primeiro, ",[40,461,180],{}," também nasce sorteado (",[40,464,196],{},") dentro do ",[40,467,176],{},", não mais hard-coded no ",[40,470,471],{},"__init__",": ninguém mais escolhe as retas escondidas na mão. Segundo, e essa é a parte que interessa: dentro do ",[40,474,475],{},"backward",", depois de calcular ",[40,478,479],{},"ho_grad"," pra camada de saída (exatamente a mesma conta de sempre, ",[40,482,483],{},"Hb.T @ erro","), tem mais três linhas, que o próprio professor cercou com um comentário digno de destaque no meio do código.",[486,487,489],"h3",{"id":488},"o-que-essas-três-linhas-fazem-sem-pular-nenhuma","O que essas três linhas fazem, sem pular nenhuma",[11,491,492,495,496,499,500],{},[40,493,494],{},"ho_delta = y - y_pred"," é o erro da saída, um número por ponto, ",[14,497,498],{"href":204},"a mesma diferença entre certo e previsto de sempre",". A pergunta que essas três linhas respondem é: ",[20,501,502],{},"cada neurônio escondido causou quanto desse erro?",[33,504,506],{"className":35,"code":505,"language":37,"meta":38,"style":38},"ih_delta = ho_delta @ self.how[1:].T * tanh_grad\n",[40,507,508],{"__ignoreMap":38},[43,509,510],{"class":45,"line":46},[43,511,505],{},[11,513,514,515,518,519,522,523,526,527,529,530,533],{},"Pensa em cada neurônio escondido como alguém que dá um palpite, e a camada de saída como quem pesa os dois palpites (",[40,516,517],{},"how[1:]",", os pesos de saída sem o bias) pra chegar numa decisão final. Se a decisão final errou, faz sentido que o palpite que mais pesou na hora de decidir carregue mais culpa, e o que quase não pesou (peso perto de zero) saia quase isento. É exatamente essa reparte que ",[40,520,521],{},"ho_delta @ self.how[1:].T"," calcula: pega o erro da saída e distribui ele entre os dois neurônios escondidos, na proporção exata dos pesos que a camada de saída deu a cada um. Repara na simetria bonita: na ida (",[40,524,525],{},"forward","), ",[40,528,192],{}," pega dois números escondidos e combina eles num só, de saída. Na volta, ele faz o caminho inverso, pega um número de erro e reparte ele em dois, um pra cada neurônio que ajudou a gerar aquela saída. Por isso o ",[40,531,532],{},".T",": é a mesma matriz, usada ao contrário.",[11,535,536,537,185,540,543,544,546,547,550,551,553,554,557,558,561,562,564,565,568,569,572],{},"Só que reparte a culpa não basta. Um neurônio só consegue de fato mudar de opinião se ainda tinha \"folga\" pra isso, e é aí que entra ",[40,538,539],{},"tanh_grad = (1 - self.H**2)",[14,541,542],{"href":16},"a derivada da tanh"," calculada em cima da própria ativação ",[40,545,249],{}," (uma identidade da tanh garante que ",[40,548,549],{},"tanh'(z) = 1 - tanh(z)²",", então não precisa guardar a pré-ativação separada). Quando um neurônio está saturado, ",[40,552,249],{}," perto de ",[40,555,556],{},"+1"," ou ",[40,559,560],{},"-1",", essa derivada fica perto de zero: por mais culpa que ele carregue, o ajuste que ele recebe é minúsculo, porque ele já tomou a decisão dele com convicção total e mexer nos pesos dele agora quase não move a saída. Quando ",[40,563,249],{}," está perto de ",[40,566,567],{},"0",", no meio do caminho, a derivada é máxima (",[40,570,571],{},"1","): esse neurônio ainda está \"indeciso\", e é justamente aí que um empurrão faz mais diferença.",[11,574,575,576,579,580,583,584,586],{},"Multiplicando as duas coisas (culpa repartida × folga que sobrou), chega o ",[40,577,578],{},"ih_delta",", e daí em diante é o Adaline de sempre: ",[40,581,582],{},"ih_grad = self.Xb.T @ ih_delta"," é a mesma regra delta, ",[40,585,208],{},", agora aplicada por neurônio escondido, usando o erro que acabou de ser calculado especialmente pra ele.",[11,588,589,590,592,593,592,595,597,598,601,602,604,605,607,608,611,612,614],{},"Isso tem nome em matemática: multiplicar derivadas de funções encadeadas (a saída depende de ",[40,591,192],{},", que depende de ",[40,594,249],{},[40,596,180],{},") pra achar o quanto lá no fundo da cadeia afeta o resultado final é a ",[228,599,600],{},"regra da cadeia",". ",[40,603,521],{}," é a derivada da saída em relação a ",[40,606,249],{},"; ",[40,609,610],{},"tanh_grad"," é a derivada de ",[40,613,249],{}," em relação à pré-ativação escondida; multiplicar as duas, ponto a ponto, é literalmente aplicar a regra da cadeia pra descobrir o quanto a pré-ativação de cada neurônio escondido pesou no erro final. Backpropagation não inventa uma matemática nova, ele só organiza a regra da cadeia numa ordem prática: calcula do fim pra trás, reaproveitando a mesma conta de cada camada pra achar a de trás dela.",[223,616,617],{},[11,618,619,620,623],{},"É esse bloco de três linhas, achar o erro que cabe a cada neurônio escondido e ajustar ele só na medida da folga que ele tinha, que o professor batizou no próprio código de ",[40,621,622],{},"### PULO DO GATO ###",". Ele não está exagerando: isso é, literalmente, a ideia inteira por trás de backpropagation. Treinar só a camada de saída (Aula 5a) é regra delta pura. Treinar a rede inteira é regra delta aplicada camada por camada, cada uma usando o erro que a camada seguinte repassou pra ela. Não tem mágica nova nenhuma, só a mesma conta, encadeada.",[223,625,626],{},[11,627,628,231,630,235,632,239],{},[228,629,230],{},[228,631,234],{},[40,633,634],{},"[[-1.396], [2.145], [-1.883]]",[25,636,638],{"id":637},"vendo-o-pulo-do-gato-acontecer","Vendo o pulo do gato acontecer",[11,640,641,642,645,646,649],{},"Testa você mesmo. O modelo abaixo começa com a caixinha desmarcada, exatamente a Aula 5a: as duas retas escondidas nascem já nos valores escolhidos na mão, e ficam ",[228,643,644],{},"fixas"," o tempo todo, só a camada de saída se ajusta a cada clique. Marca a caixinha e treina de novo: agora virou a Aula 5b, e as duas retas escondidas ",[228,647,648],{},"também"," se mexem a cada passo, puxadas pelo erro que a camada de saída manda de volta pra elas.",[651,652],"mlp-trainer",{":classes":653,":learning-rate":654,":points":655,":steps-per-click":656,":x-max":657,":x-min":658,":y-max":657,":y-min":658,"accuracy-readout-label":659,"architecture-hidden":660,"architecture-labels":661,"negative-label":662,"positive-label":663,"reset-label":664,"step-label":665,"steps-readout-label":666,"x-label":212,"y-label":216,":allow-freeze-toggle":667,":fixed-hidden-weights":184,"freeze-toggle-label":668},"[-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.01","[[0.1095, 0.225], [0.0069, 0.1992], [0.0741, 0.0738], [0.29, 0.1158], [0.0957, 0.1729], [0.2127, 0.0969], [0.1588, 0.0026], [0.0633, 0.0562], [-0.0332, -0.0509], [-0.0627, 0.0761], [0.8828, 0.068], [0.9069, -0.0336], [0.8921, 0.1238], [0.9751, 0.0154], [0.86, -0.1015], [0.8496, -0.1197], [0.7581, 0.2102], [0.6798, 0.1799], [0.9328, 0.0688], [0.9459, 0.1527], [0.2045, 0.877], [0.0408, 0.8395], [0.0014, 0.8955], [0.0214, 1.0069], [-0.0869, 0.7906], [0.0047, 0.6907], [0.2902, 0.6592], [0.0717, 0.8475], [0.2656, 0.7015], [0.2072, 0.8269], [0.8845, 0.8329], [0.964, 0.7862], [0.8921, 0.9353], [1.084, 0.6595], [1.0525, 0.9948], [0.8516, 0.9305], [0.8534, 1.0647], [0.921, 0.8784], [0.8772, 0.88], [0.8823, 0.8121]]","500","1.5","-0.5","acurácia no dataset","2","2 neurônios escondidos","falso (XOR)","verdadeiro (XOR)","Reiniciar (novos pesos aleatórios)","Treinar mais passos","passos de treino","true","também treinar a camada escondida (backpropagation)",[11,670,671,672,675,676,678,679,213,681,683,684,686,687,690],{},"Com a camada escondida congelada, a região de decisão sempre parte das mesmas duas retas paralelas, só a inclinação da fronteira final entre elas muda de clique pra clique. Com a caixinha marcada, repara que ",[228,673,674],{},"as próprias retas escondidas mudam de posição"," enquanto treina, às vezes até cruzam, o gradiente que chega nelas via ",[40,677,578],{}," está genuinamente redesenhando a transformação de espaço, não só reajustando o que vem depois dela. E como o sorteio inicial de ",[40,680,180],{},[40,682,192],{}," é aleatório, às vezes o treino empaca num mínimo ruim antes de chegar em acurácia ",[40,685,234],{},", isso também é real: gradiente descendente não tem a garantia de solução exata que ",[14,688,689],{"href":16},"a pseudo-inversa do ELM tinha",". Se travar, o botão de reiniciar sorteia pesos novos e tenta de novo.",[25,692,694],{"id":693},"fechando","Fechando",[696,697,698,712],"table",{},[699,700,701],"thead",{},[702,703,704,709],"tr",{},[705,706,708],"th",{"align":707},"left","O que eu já sabia",[705,710,711],{"align":707},"O que essa aula assentou",[713,714,715,724,737],"tbody",{},[702,716,717,721],{},[718,719,720],"td",{"align":707},"Regra delta treina a camada de saída sozinha (Adaline, ELM)",[718,722,723],{"align":707},"A mesma regra delta treina a camada escondida também, desde que o erro chegue até ela",[702,725,726,731],{},[718,727,728,730],{"align":707},[40,729,192],{}," combina os neurônios escondidos numa saída (forward)",[718,732,733,734,736],{"align":707},"A mesma matriz ",[40,735,192],{},", transposta, reparte o erro de volta entre os neurônios (backward), o pulo do gato",[702,738,739,742],{},[718,740,741],{"align":707},"Derivada da tanh mede o quanto uma ativação satura",[718,743,744],{"align":707},"Essa derivada também mede o quanto um neurônio escondido ainda tem \"folga\" pra ser corrigido",[25,746,748],{"id":747},"aplicação-prática","Aplicação Prática",[11,750,751,754,755,759,760,763,764,767,768,771],{},[14,752,753],{"href":16},"No post passado",", testei o ELM (camada escondida sorteada, resolvida por pseudo-inversa) no dataset de duas luas e vi a acurácia subir até 50 neurônios e cair de novo em 100, ",[14,756,758],{"href":757},"\u002Fplaylists\u002Fmachine-learning-specialization\u002Fw2-lab04-feature-engineering","overfitting clássico",". Agora que sei treinar a camada escondida de verdade, cabe a pergunta óbvia: será que treinar bate sortear? Rodei o mesmo ",[40,761,762],{},"SingleHiddenLayer"," (",[40,765,766],{},"learning_rate"," ajustada pra cada tamanho, ",[40,769,770],{},"3000"," passos, 10 sementes de inicialização por tamanho) no mesmo dataset, mesma divisão treino\u002Fteste 70\u002F30:",[696,773,774,788],{},[699,775,776],{},[702,777,778,781,785],{},[705,779,780],{"align":707},"Neurônios escondidos",[705,782,784],{"align":783},"right","ELM (post passado)",[705,786,787],{"align":783},"Backprop (esta aula)",[713,789,790,802,815,828,838],{},[702,791,792,794,797],{},[718,793,571],{"align":707},[718,795,796],{"align":783},"0.714",[718,798,799],{"align":783},[228,800,801],{},"0.880",[702,803,804,807,810],{},[718,805,806],{"align":707},"5",[718,808,809],{"align":783},"0.881",[718,811,812],{"align":783},[228,813,814],{},"0.969",[702,816,817,820,823],{},[718,818,819],{"align":707},"20",[718,821,822],{"align":783},"0.927",[718,824,825],{"align":783},[228,826,827],{},"0.968",[702,829,830,833,836],{},[718,831,832],{"align":707},"50",[718,834,835],{"align":783},"0.930",[718,837,827],{"align":783},[702,839,840,843,846],{},[718,841,842],{"align":707},"100",[718,844,845],{"align":783},"0.843",[718,847,848],{"align":783},[228,849,850],{},"0.976",[11,852,853,854,856,857,860,861,864,865,867,868,871],{},"Duas coisas saltam aos olhos. A primeira: com só ",[228,855,571],{}," neurônio, o backprop já bate ",[40,858,859],{},"0.88",", bem acima do ",[40,862,863],{},"0.71"," do ELM, porque aqui aquele único neurônio não fica preso numa direção sorteada ao acaso, o gradiente empurra ele pra a direção que realmente separa as luas. A segunda, mais sutil: o backprop não despenca em ",[40,866,842],{}," neurônios como o ELM despencou. A pseudo-inversa do ELM ",[228,869,870],{},"sempre"," encontra a solução exata pro conjunto de treino, ajusta perfeitamente até o ruído, com rede grande demais isso vira overfitting puro. O gradiente descendente, rodando um número fixo de passos, quase nunca chega nessa solução exata, ele para no meio do caminho, e parar no meio do caminho, aqui, funciona como um freio contra decorar o treino que a conta fechada da pseudo-inversa não tem.",[11,873,874,875,877,878,881],{},"Agora treina ao vivo, você mesmo, no navegador: o modelo abaixo é o mesmo ",[40,876,762],{},", rodando de verdade em cima dos ",[228,879,880],{},"300 pontos"," das duas luas (os mesmos que gerei pra tabela acima, treino e teste juntos, já que aqui o ponto é ver o gradiente trabalhar, não medir generalização de novo):",[651,883],{":classes":884,":learning-rate":654,":points":885,":steps-per-click":886,":x-max":887,":x-min":888,":y-max":889,":y-min":890,"accuracy-readout-label":659,"architecture-hidden":819,"architecture-labels":891,"negative-label":892,"positive-label":893,"reset-label":664,"step-label":665,"steps-readout-label":666,"x-label":212,"y-label":216},"[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, 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, -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, -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.3303, -0.5899], [1.9014, -0.2758], [0.736, -0.5258], [1.0932, -0.3848], [1.297, -0.3973], [-1.0288, 0.4776], [1.7042, 0.2147], [0.663, 0.92], [-0.37, 0.5763], [0.5704, 0.4955], [1.4513, -0.2351], [1.6527, -0.0843], [0.1298, 0.214], [1.34, -0.7482], [1.6996, 0.3089], [1.0004, 0.7692], [-0.5497, 1.0832], [-1.0964, 0.6094], [1.7485, -0.1153], [1.6062, -0.2096], [0.1381, -0.0817], [-0.1268, -0.2681], [1.0229, 0.9121], [1.6388, -0.7464], [1.3012, -0.4285], [1.9126, -0.2328], [0.2557, -0.5603], [0.7759, 0.7043], [2.6068, 0.5388], [0.9672, -0.3322], [0.6375, 0.2643], [0.4069, -0.2066], [1.1865, 0.1902], [0.3515, 0.9889], [0.1085, -0.1568], [2.0375, 0.2391], [-0.5971, 1.0033], [1.8859, -0.0487], [0.5526, -0.4589], [0.8658, -0.0051], [0.7075, 0.5698], [0.0481, 0.7484], [0.3433, 0.7872], [-0.9295, 0.0356], [0.9925, 0.162], [-0.547, 1.0634], [0.8369, 0.8404], [0.2266, -0.1316], [0.8045, -0.1066], [0.2584, 0.0958], [-1.4088, 0.4733], [-0.0728, 1.1705], [0.2208, 0.2515], [0.2028, 1.1456], [0.7741, -0.7957], [1.1044, 0.0784], [-0.9682, 0.2852], [0.8538, 1.0361], [0.2042, -0.2637], [-0.7407, 0.0137], [-0.1896, 0.9366], [0.8404, -0.4688], [0.9719, 0.1966], [-0.5388, 0.2359], [0.1584, 0.5568], [0.0258, 1.5023], [-0.2582, 0.7805], [1.0739, -0.5138], [1.9507, 0.4364], [0.9478, -0.6541], [0.7503, -0.4379], [-0.3903, 1.2044], [1.6477, -0.53], [0.6338, 0.5721], [2.2086, -0.2275], [0.7179, 0.3054], [-0.5442, 0.0401], [0.5366, -0.5826], [1.9594, -0.1354], [0.8213, -0.2461], [0.3132, -0.0079], [0.1684, 1.0608], [1.6336, -0.5309], [1.1523, 0.0126], [-0.4425, 0.7252], [1.6572, 0.4493], [-0.0338, -0.1808], [0.4295, -0.5755], [1.7466, -0.3118], [0.9422, 0.1132], [2.1034, 0.8712], [1.44, -0.571], [-0.1693, 1.1099], [1.9739, -0.2091], [1.7222, 0.8922], [-0.063, 1.1117], [-1.0161, 0.0234], [0.4211, -0.8427], [-0.8704, 0.0985], [1.0073, -0.3167], [0.0793, 0.8474], [0.1887, 0.4901], [-1.0311, 0.0364], [0.419, 1.2454], [0.3118, -0.068], [-0.7692, 0.7505], [1.366, -0.3429], [0.4818, 0.9663], [-0.1303, 0.4931], [-0.065, 1.5288], [0.5386, 0.7792], [0.1505, 0.5831], [-0.9679, 0.9883], [0.885, 1.2531], [0.2234, 0.9243], [-0.1503, 1.3547], [0.6147, 0.5638], [0.3254, 0.6433], [1.0539, 0.5349], [-0.4373, 1.4655], [-0.8859, 0.3428], [-0.0618, 0.4393], [0.9581, -0.3041], [0.675, 0.8379], [-0.6414, 1.236], [-0.6717, 1.0456], [0.6577, -0.336], [1.0525, 0.3961], [0.234, 1.2805], [1.0472, 0.5858], [1.1562, 0.1538], [1.0589, -0.1424], [-0.0965, 0.9135], [2.0149, 0.108], [1.1108, -0.1541], [2.0939, -0.221], [0.8512, -0.0971], [-0.6361, 0.588], [2.0961, -0.0864], [0.1287, 0.8629], [-0.636, 0.8754], [0.7851, -0.2047], [-1.0945, 0.3967], [0.4381, 0.475], [1.1068, -0.2732], [0.123, 0.0062], [0.2951, -0.4544], [0.2577, -0.0222], [0.4909, 0.8782], [2.4451, 0.5607], [0.4352, 0.313], [0.8805, 0.5359], [0.52, 1.1687], [0.7452, -0.4927], [0.3441, -0.3079], [-1.0255, 0.5466], [1.1422, -0.7754], [-1.0229, 0.6241], [0.8922, 0.8983], [0.5656, -0.1002], [-0.027, 0.0918], [0.0842, 0.0527], [0.4515, -0.3718], [1.6476, -0.1574], [1.1743, -0.5677], [-0.3606, 0.9006], [0.2095, 0.856], [1.9257, 0.7966], [1.3821, -0.3752], [1.2235, -0.1019], [0.5867, -0.5357], [0.3355, -0.0258], [-1.1089, 1.3133], [0.964, -0.0432], [0.3886, 0.094], [1.1327, 0.1394], [1.8824, -0.1544], [0.665, 0.8422], [1.9627, 0.1891], [0.4841, 0.6735], [0.8908, 0.3358], [2.4238, 0.5666], [0.4051, 1.1354], [-0.1751, 1.0858], [0.4729, -0.2232], [0.642, 1.0712], [0.7468, 0.6279], [1.8426, -0.1], [1.0925, -0.0676], [-0.0057, -0.2895], [-0.5672, 0.0938], [-0.2488, 1.2806], [-0.5468, 0.9202], [1.6941, 0.2019], [-0.3109, 1.114], [1.911, 0.7004], [-0.6134, 0.7344], [-0.9224, 1.0634], [-1.5075, -0.1456], [0.172, -0.2075], [0.7919, 0.6689], [-0.2754, 0.5253], [0.0789, 0.5625], [0.1438, 0.2929], [-0.8682, 0.8606], [0.7005, -0.5109], [-1.5306, 0.1861], [1.1631, 0.5677], [0.811, 1.041], [0.9322, 0.6331], [0.0766, 1.1215], [1.4684, -0.5449], [1.8967, -0.416], [0.783, -0.9042], [0.9339, 0.5765], [1.1038, -0.7992], [0.4435, -0.0397], [1.8049, -0.2145], [-1.0166, -0.274], [1.0949, 0.0657], [1.5656, -0.7872], [-0.7205, 0.8522], [0.2886, 0.1793], [0.3963, -0.2732], [0.9452, 0.783], [2.2297, 0.2908], [0.5376, -1.1409], [0.561, 0.6498], [1.3516, -0.2948], [1.6055, 0.4503], [-0.2031, 0.8135], [-0.4008, 0.8198], [-0.7298, 0.9195], [-1.1081, 0.7349], [-0.1001, 1.0787], [-1.1249, -0.0847], [1.8147, -0.2369], [0.9115, -0.7904], [0.7909, 1.1051], [-1.0433, 0.8665], [0.196, -0.1538], [1.2146, -0.7157], [-0.248, 0.7318], [2.1267, 0.2264], [1.0913, -0.1044], [0.3602, -0.1025], [0.0758, 1.2059], [1.8839, -0.2401], [1.0568, 0.3327], [0.8952, 0.3524], [-0.2173, 1.0444], [1.0191, 0.2637], [-1.2687, -0.2351], [-0.0289, -0.2932], [1.0811, -0.0036], [0.1454, -0.0599], [-0.2277, 0.9288], [0.816, -0.151], [-1.0776, 0.4302], [0.1677, 0.1941], [-0.0185, 0.8674], [0.3464, 0.5976], [-1.1729, 0.1982], [-1.116, 0.0217], [1.2626, -0.0665], [-0.3579, 0.6685], [2.0915, -0.6603], [-0.2996, 1.1704], [1.7006, 0.121], [0.2191, -0.2089], [-0.552, 0.8547], [0.3358, 0.6574], [-0.6488, 0.4885], [-0.7485, 0.5053], [2.0903, 0.2882], [-0.725, 0.4276], [1.0254, -0.5216], [0.1643, -0.3061], [-0.1276, 0.1627], [0.7628, -0.4966], [0.2614, -0.0673], [1.8751, -0.0208], [1.7354, -0.9256], [0.1893, -0.4103], [0.0083, 0.4903], [-0.964, 0.3169], [0.6568, -0.3799], [-1.2147, 0.6795], [0.2585, 0.3218], [0.7737, 0.9318], [1.0474, 0.4956], [0.9547, -0.6387], [1.2767, -0.1488], [1.018, -0.2228], [1.9391, 0.2873], [1.6411, -0.1644], [1.4701, -0.3995], [0.6683, -0.1564], [-0.8356, 0.7715], [0.9148, 0.7828]]","1000","2.7","-1.6","1.6","-1.2","20 neurônios escondidos","lua 0","lua 1",[11,895,896],{},"Clica em \"treinar mais passos\" algumas vezes: dá pra ver a fronteira sair de um emaranhado praticamente aleatório e se curvar em volta das duas luas, sem que ninguém tenha escolhido essas 20 direções escondidas na mão nem sorteado elas de vez, o próprio gradiente as encontrou. A acurácia mostrada aqui é em cima do próprio dataset de treino ao vivo, não é diretamente comparável linha a linha com a tabela acima (que mediu em teste separado), mas o comportamento é o mesmo: poucos cliques já resolvem a maior parte, e depois de um tempo a curva satura.",[898,899,900],"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":38,"searchDepth":52,"depth":52,"links":902},[903,904,907,908,909],{"id":27,"depth":52,"text":28},{"id":253,"depth":52,"text":254,"children":905},[906],{"id":488,"depth":58,"text":489},{"id":637,"depth":52,"text":638},{"id":693,"depth":52,"text":694},{"id":747,"depth":52,"text":748},null,"2026-09-02","Aula 5a e 5b: depois de ver a camada escondida sorteada (ELM) ou escolhida na mão, chega a vez dela aprender de verdade, o erro final é repartido entre os neurônios escondidos e volta pra ajustar cada um deles. É esse mecanismo que o próprio professor batiza, no código, de pulo do gato.","md",{},"\u002Fpt\u002Fplaylists\u002Fneural-networks\u002Fcamada-escondida-tambem-aprende","neural-networks",{"title":6,"description":912},"published","pt\u002Fplaylists\u002Fneural-networks\u002Fcamada-escondida-tambem-aprende",[921,922,923,924],"mlp","backpropagation","regra-da-cadeia","gradiente","CEp-ffERmrO5QDJafy8jJ9tFUnUxkZtdl-dv0amiDs4",1788965098930]