[{"data":1,"prerenderedAt":1159},["ShallowReactive",2],{"lang-switch-post-\u002Fplaylists\u002Fpattern-recognition\u002Fclassification-threshold":3,"post-pt-pattern-recognition-classification-threshold":4},"\u002Fen\u002Fplaylists\u002Fpattern-recognition\u002Fclassification-threshold",{"id":5,"title":6,"body":7,"cover":1144,"date":1145,"description":1146,"extension":1147,"meta":1148,"navigation":185,"order":144,"path":1149,"playlist":1150,"seo":1151,"status":1152,"stem":1153,"tags":1154,"__hash__":1158},"posts\u002Fpt\u002Fplaylists\u002Fpattern-recognition\u002Fclassification-threshold.md","Classificação com um Limiar: Quando Regressão Vira Decisão",{"type":8,"value":9,"toc":1132},"minimark",[10,20,25,33,49,71,77,81,92,101,112,116,126,148,159,166,193,203,236,240,243,297,312,741,745,748,757,767,790,794,812,837,852,867,871,878,898,909,917,921,937,956,963,966,973,986,1009,1013,1062,1065,1069,1075,1095,1110,1122,1128],[11,12,13,14,19],"p",{},"Aula 3, dividida em duas partes: classificação (3a) e normalização (3b). E o professor começa de um jeito provocador, reaproveitando a mesma classe de regressão ",[15,16,18],"a",{"href":17},"\u002Fplaylists\u002Fpattern-recognition\u002Fnormal-equation","do post anterior"," pra resolver um problema que não é bem de regressão.",[21,22,24],"h2",{"id":23},"o-dataset-câncer-de-mama-569-pacientes","O dataset: câncer de mama, 569 pacientes",[11,26,27,28,32],{},"O professor troca de dataset: agora é o ",[29,30,31],"code",{},"load_breast_cancer"," do scikit-learn, 569 pacientes, 30 variáveis medidas a partir de imagem de biópsia (raio, textura, perímetro, área, e por aí vai), e um alvo binário, maligno ou benigno. Antes de escolher qualquer variável, ele calcula a correlação de cada uma das 30 com o alvo e ordena.",[34,35,40],"pre",{"className":36,"code":37,"language":38,"meta":39,"style":39},"language-python shiki shiki-themes github-light github-dark","correlations = df.corr()['target'].drop('target')\n","python","",[29,41,42],{"__ignoreMap":39},[43,44,47],"span",{"class":45,"line":46},"line",1,[43,48,37],{},[50,51,52],"blockquote",{},[11,53,54,58,59,62,63,66,67,70],{},[55,56,57],"strong",{},"Saída:"," a mais correlacionada é ",[29,60,61],{},"worst concave points"," (-0.79), seguida de ",[29,64,65],{},"worst perimeter"," (-0.78) e ",[29,68,69],{},"mean concave points"," (-0.78). Faz sentido clinicamente: pontos de concavidade no contorno do tumor e o perímetro dele são exatamente o tipo de coisa que um patologista olha pra suspeitar de malignidade.",[11,72,73,74,76],{},"Ele fica com ",[29,75,61],{}," como a única variável de entrada por enquanto, guardando as outras 29 pra usar mais adiante.",[21,78,80],{"id":79},"o-alvo-binário-invertido-de-propósito","O alvo binário, invertido de propósito",[11,82,83,84,87,88,91],{},"O scikit-learn já vem com uma convenção pra esse dataset: ",[29,85,86],{},"target=0"," é maligno, ",[29,89,90],{},"target=1"," é benigno. O professor inverte isso na mão:",[34,93,95],{"className":36,"code":94,"language":38,"meta":39,"style":39},"y = np.array(y==0, dtype=int)  # agora 1 = maligno\n",[29,96,97],{"__ignoreMap":39},[43,98,99],{"class":45,"line":46},[43,100,94],{},[11,102,103,104,107,108,111],{},"Por quê? Porque a convenção mais comum em problema de detecção (fraude, doença, defeito) é fazer o rótulo ",[29,105,106],{},"1"," representar o caso que você quer ",[55,109,110],{},"flagar",", o evento raro e preocupante, não o caso \"normal\". Depois da troca, a fração de pacientes malignos é 0.373, ou seja, 37% dos 569 casos.",[21,113,115],{"id":114},"regressão-pra-classificar","Regressão pra classificar?",[11,117,118,119,125],{},"Aqui que a aula fica provocadora. Em vez de criar um classificador do zero, o professor reaproveita a ",[55,120,121,122],{},"mesma classe ",[29,123,124],{},"LinearRegressor"," do post anterior (a que resolve por pseudo-inversa) e simplesmente treina ela em cima do alvo 0\u002F1, como se fosse um número contínuo qualquer:",[34,127,129],{"className":36,"code":128,"language":38,"meta":39,"style":39},"regressor = LinearRegressor()\nregressor.fit(X_train, y_train)\ny_pred = regressor.predict(X_train)\n",[29,130,131,136,142],{"__ignoreMap":39},[43,132,133],{"class":45,"line":46},[43,134,135],{},"regressor = LinearRegressor()\n",[43,137,139],{"class":45,"line":138},2,[43,140,141],{},"regressor.fit(X_train, y_train)\n",[43,143,145],{"class":45,"line":144},3,[43,146,147],{},"y_pred = regressor.predict(X_train)\n",[50,149,150],{},[11,151,152,154,155,158],{},[55,153,57],{}," ",[29,156,157],{},"w_ = [-0.303, 5.844]",", MSE = 0.0863.",[11,160,161,162,165],{},"Um MSE de 0.0863 parece ótimo à primeira vista (é bem menor que 1), mas repara no que acontece quando o professor mede a ",[55,163,164],{},"acurácia"," desse resultado:",[34,167,169],{"className":36,"code":168,"language":38,"meta":39,"style":39},"def accuracy(y, y_pred):\n    return np.sum(y == y_pred) \u002F len(y)\n\nprint(accuracy(y_train, y_pred))\n",[29,170,171,176,181,187],{"__ignoreMap":39},[43,172,173],{"class":45,"line":46},[43,174,175],{},"def accuracy(y, y_pred):\n",[43,177,178],{"class":45,"line":138},[43,179,180],{},"    return np.sum(y == y_pred) \u002F len(y)\n",[43,182,183],{"class":45,"line":144},[43,184,186],{"emptyLinePlaceholder":185},true,"\n",[43,188,190],{"class":45,"line":189},4,[43,191,192],{},"print(accuracy(y_train, y_pred))\n",[50,194,195],{},[11,196,197,154,199,202],{},[55,198,57],{},[29,200,201],{},"0.0",". Zero por cento de acerto.",[11,204,205,206,209,210,213,214,217,218,221,222,217,225,227,228,231,232,235],{},"Isso não é um bug, é uma pegadinha proposital. ",[29,207,208],{},"y_pred"," é a saída ",[55,211,212],{},"contínua"," da regressão, tipo ",[29,215,216],{},"0.312"," ou ",[29,219,220],{},"0.847",", nunca exatamente ",[29,223,224],{},"0",[29,226,106],{},". Comparar ",[29,229,230],{},"y == y_pred"," com ",[29,233,234],{},"=="," entre um inteiro e um float quase nunca dá igual, então a acurácia calculada desse jeito sempre vai bater zero, não importa o quão bom o modelo seja. Falta uma peça: transformar aquele número contínuo numa decisão.",[21,237,239],{"id":238},"o-limiar-de-número-contínuo-pra-decisão","O limiar: de número contínuo pra decisão",[11,241,242],{},"A correção é pequena: comparar a saída da regressão com 0.5 antes de virar rótulo.",[34,244,246],{"className":36,"code":245,"language":38,"meta":39,"style":39},"class LinearClassifier(BaseEstimator, ClassifierMixin):\n    def fit(self, X, y):\n        X = include_bias(X)\n        self.w_ = np.linalg.pinv(X) @ y\n        return self\n    def predict(self, X):\n        X = include_bias(X)\n        y_pred = X @ self.w_\n        return (y_pred.reshape(X.shape[0],) > 0.5).astype(int)\n",[29,247,248,253,258,263,268,274,280,285,291],{"__ignoreMap":39},[43,249,250],{"class":45,"line":46},[43,251,252],{},"class LinearClassifier(BaseEstimator, ClassifierMixin):\n",[43,254,255],{"class":45,"line":138},[43,256,257],{},"    def fit(self, X, y):\n",[43,259,260],{"class":45,"line":144},[43,261,262],{},"        X = include_bias(X)\n",[43,264,265],{"class":45,"line":189},[43,266,267],{},"        self.w_ = np.linalg.pinv(X) @ y\n",[43,269,271],{"class":45,"line":270},5,[43,272,273],{},"        return self\n",[43,275,277],{"class":45,"line":276},6,[43,278,279],{},"    def predict(self, X):\n",[43,281,283],{"class":45,"line":282},7,[43,284,262],{},[43,286,288],{"class":45,"line":287},8,[43,289,290],{},"        y_pred = X @ self.w_\n",[43,292,294],{"class":45,"line":293},9,[43,295,296],{},"        return (y_pred.reshape(X.shape[0],) > 0.5).astype(int)\n",[50,298,299],{},[11,300,301,303,304,307,308,311],{},[55,302,57],{}," acurácia de treino agora é ",[55,305,306],{},"0.9165",". No conjunto de teste, ",[55,309,310],{},"0.8947",".",[11,313,314,315,318,319,324,325,555,556,600,601,604,605,672,673,740],{},"O mesmo ajuste (o mesmo ",[29,316,317],{},"w_","), só que agora com um limiar decidindo o rótulo final. É exatamente o que o Bishop chama de ",[320,321,323],"glossary-term",{"definition":322},"uma função y(x) = w·x + w0 que decide a classe olhando o sinal (ou, como aqui, se passa de um limiar): de um lado da fronteira é uma classe, do outro é a outra","função discriminante linear",": ",[43,326,329,390],{"className":327},[328],"katex",[43,330,333],{"className":331},[332],"katex-mathml",[334,335,337],"math",{"xmlns":336},"http:\u002F\u002Fwww.w3.org\u002F1998\u002FMath\u002FMathML",[338,339,340,385],"semantics",{},[341,342,343,347,352,356,359,362,372,374,377],"mrow",{},[344,345,346],"mi",{},"y",[348,349,351],"mo",{"stretchy":350},"false","(",[344,353,355],{"mathvariant":354},"bold","x",[348,357,358],{"stretchy":350},")",[348,360,361],{},"=",[363,364,365,368],"msup",{},[344,366,367],{"mathvariant":354},"w",[344,369,371],{"mathvariant":370},"sans-serif","T",[344,373,355],{"mathvariant":354},[348,375,376],{},"+",[378,379,380,382],"msub",{},[344,381,367],{},[383,384,224],"mn",{},[386,387,389],"annotation",{"encoding":388},"application\u002Fx-tex","y(\\mathbf{x}) = \\mathbf{w}^{\\mathsf{T}}\\mathbf{x} + w_0",[43,391,395,434,501],{"className":392,"ariaHidden":394},[393],"katex-html","true",[43,396,399,404,410,414,418,422,427,431],{"className":397},[398],"base",[43,400],{"className":401,"style":403},[402],"strut","height:1em;vertical-align:-0.25em;",[43,405,346],{"className":406,"style":409},[407,408],"mord","mathnormal","margin-right:0.0359em;",[43,411,351],{"className":412},[413],"mopen",[43,415,355],{"className":416},[407,417],"mathbf",[43,419,358],{"className":420},[421],"mclose",[43,423],{"className":424,"style":426},[425],"mspace","margin-right:0.2778em;",[43,428,361],{"className":429},[430],"mrel",[43,432],{"className":433,"style":426},[425],[43,435,437,441,487,490,494,498],{"className":436},[398],[43,438],{"className":439,"style":440},[402],"height:0.9324em;vertical-align:-0.0833em;",[43,442,444,448],{"className":443},[407],[43,445,367],{"className":446,"style":447},[407,417],"margin-right:0.016em;",[43,449,452],{"className":450},[451],"msupsub",[43,453,456],{"className":454},[455],"vlist-t",[43,457,460],{"className":458},[459],"vlist-r",[43,461,465],{"className":462,"style":464},[463],"vlist","height:0.8491em;",[43,466,468,473],{"style":467},"top:-3.063em;margin-right:0.05em;",[43,469],{"className":470,"style":472},[471],"pstrut","height:2.7em;",[43,474,480],{"className":475},[476,477,478,479],"sizing","reset-size6","size3","mtight",[43,481,483],{"className":482},[407,479],[43,484,371],{"className":485},[407,486,479],"mathsf",[43,488,355],{"className":489},[407,417],[43,491],{"className":492,"style":493},[425],"margin-right:0.2222em;",[43,495,376],{"className":496},[497],"mbin",[43,499],{"className":500,"style":493},[425],[43,502,504,508],{"className":503},[398],[43,505],{"className":506,"style":507},[402],"height:0.5806em;vertical-align:-0.15em;",[43,509,511,515],{"className":510},[407],[43,512,367],{"className":513,"style":514},[407,408],"margin-right:0.0269em;",[43,516,518],{"className":517},[451],[43,519,522,546],{"className":520},[455,521],"vlist-t2",[43,523,525,541],{"className":524},[459],[43,526,529],{"className":527,"style":528},[463],"height:0.3011em;",[43,530,532,535],{"style":531},"top:-2.55em;margin-left:-0.0269em;margin-right:0.05em;",[43,533],{"className":534,"style":472},[471],[43,536,538],{"className":537},[476,477,478,479],[43,539,224],{"className":540},[407,479],[43,542,545],{"className":543},[544],"vlist-s","​",[43,547,549],{"className":548},[459],[43,550,553],{"className":551,"style":552},[463],"height:0.15em;",[43,554],{},", e o ponto onde ",[43,557,559,579],{"className":558},[328],[43,560,562],{"className":561},[332],[334,563,564],{"xmlns":336},[338,565,566,576],{},[341,567,568,570,572,574],{},[344,569,346],{},[348,571,351],{"stretchy":350},[344,573,355],{"mathvariant":354},[348,575,358],{"stretchy":350},[386,577,578],{"encoding":388},"y(\\mathbf{x})",[43,580,582],{"className":581,"ariaHidden":394},[393],[43,583,585,588,591,594,597],{"className":584},[398],[43,586],{"className":587,"style":403},[402],[43,589,346],{"className":590,"style":409},[407,408],[43,592,351],{"className":593},[413],[43,595,355],{"className":596},[407,417],[43,598,358],{"className":599},[421]," cruza o limiar de decisão é a ",[55,602,603],{},"fronteira de decisão",". No caso clássico do Bishop essa fronteira fica em ",[43,606,608,632],{"className":607},[328],[43,609,611],{"className":610},[332],[334,612,613],{"xmlns":336},[338,614,615,629],{},[341,616,617,619,621,623,625,627],{},[344,618,346],{},[348,620,351],{"stretchy":350},[344,622,355],{"mathvariant":354},[348,624,358],{"stretchy":350},[348,626,361],{},[383,628,224],{},[386,630,631],{"encoding":388},"y(\\mathbf{x})=0",[43,633,635,662],{"className":634,"ariaHidden":394},[393],[43,636,638,641,644,647,650,653,656,659],{"className":637},[398],[43,639],{"className":640,"style":403},[402],[43,642,346],{"className":643,"style":409},[407,408],[43,645,351],{"className":646},[413],[43,648,355],{"className":649},[407,417],[43,651,358],{"className":652},[421],[43,654],{"className":655,"style":426},[425],[43,657,361],{"className":658},[430],[43,660],{"className":661,"style":426},[425],[43,663,665,669],{"className":664},[398],[43,666],{"className":667,"style":668},[402],"height:0.6444em;",[43,670,224],{"className":671},[407],", aqui fica em ",[43,674,676,701],{"className":675},[328],[43,677,679],{"className":678},[332],[334,680,681],{"xmlns":336},[338,682,683,698],{},[341,684,685,687,689,691,693,695],{},[344,686,346],{},[348,688,351],{"stretchy":350},[344,690,355],{"mathvariant":354},[348,692,358],{"stretchy":350},[348,694,361],{},[383,696,697],{},"0.5",[386,699,700],{"encoding":388},"y(\\mathbf{x})=0.5",[43,702,704,731],{"className":703,"ariaHidden":394},[393],[43,705,707,710,713,716,719,722,725,728],{"className":706},[398],[43,708],{"className":709,"style":403},[402],[43,711,346],{"className":712,"style":409},[407,408],[43,714,351],{"className":715},[413],[43,717,355],{"className":718},[407,417],[43,720,358],{"className":721},[421],[43,723],{"className":724,"style":426},[425],[43,726,361],{"className":727},[430],[43,729],{"className":730,"style":426},[425],[43,732,734,737],{"className":733},[398],[43,735],{"className":736,"style":668},[402],[43,738,697],{"className":739},[407]," porque o alvo tá codificado como 0\u002F1 em vez de centralizado em zero, mas a ideia geométrica é idêntica: dos dois lados de uma linha, a decisão muda.",[21,742,744],{"id":743},"uma-identidade-elegante","Uma identidade elegante",[11,746,747],{},"O professor soma acurácia com MSE (ambos calculados sobre as previsões já limiarizadas, 0 ou 1):",[34,749,751],{"className":36,"code":750,"language":38,"meta":39,"style":39},"accuracy(y_train, y_pred) + mean_squared_error(y_train, y_pred)\n",[29,752,753],{"__ignoreMap":39},[43,754,755],{"class":45,"line":46},[43,756,750],{},[50,758,759],{},[11,760,761,154,763,766],{},[55,762,57],{},[29,764,765],{},"1.0",", exatamente.",[11,768,769,770,772,773,775,776,779,780,782,783,785,786,789],{},"Não é coincidência. Quando ",[29,771,346],{}," e ",[29,774,208],{}," são só 0 ou 1, o erro quadrático ",[29,777,778],{},"(y - y_pred)²"," vale ",[29,781,224],{}," quando acerta e ",[29,784,106],{}," quando erra, exatamente igual ao erro absoluto. Então o MSE médio é literalmente a ",[55,787,788],{},"taxa de erro",", e taxa de erro mais taxa de acerto sempre soma 1. Uma identidade boba de algebrismo, mas que mostra uma coisa importante: MSE sobre rótulo binário limiarizado é só outro nome pra \"fração de erro\".",[21,791,793],{"id":792},"o-ajuste-ótimo-pro-erro-contínuo-não-é-o-ajuste-ótimo-pra-classificar","O ajuste ótimo pro erro contínuo não é o ajuste ótimo pra classificar",[11,795,796,797,799,800,803,804,807,808,811],{},"Aqui mora o ponto mais sutil da aula. O professor pega o ",[29,798,317],{}," que a pseudo-inversa encontrou (o que minimiza o erro quadrático no alvo 0\u002F1 ",[55,801,802],{},"contínuo",") e varre valores próximos do coeficiente ",[29,805,806],{},"w_[1]",", medindo o MSE ",[55,809,810],{},"depois de limiarizar"," em cada um:",[34,813,815],{"className":36,"code":814,"language":38,"meta":39,"style":39},"w1_values = np.linspace(original_w[1] - 3, original_w[1] + 3, 100)\nfor w1_candidate in w1_values:\n    classifier.w_ = np.array([original_w[0], w1_candidate])\n    mse_values.append(mean_squared_error(y_train, classifier.predict(X_train)))\n",[29,816,817,822,827,832],{"__ignoreMap":39},[43,818,819],{"class":45,"line":46},[43,820,821],{},"w1_values = np.linspace(original_w[1] - 3, original_w[1] + 3, 100)\n",[43,823,824],{"class":45,"line":138},[43,825,826],{},"for w1_candidate in w1_values:\n",[43,828,829],{"class":45,"line":144},[43,830,831],{},"    classifier.w_ = np.array([original_w[0], w1_candidate])\n",[43,833,834],{"class":45,"line":189},[43,835,836],{},"    mse_values.append(mean_squared_error(y_train, classifier.predict(X_train)))\n",[50,838,839],{},[11,840,841,843,844,847,848,851],{},[55,842,57],{}," o menor MSE encontrado (0.0791) acontece em ",[29,845,846],{},"w_[1] = 5.5709",", não no ",[29,849,850],{},"w_[1] = 5.8436"," que a pseudo-inversa devolveu.",[11,853,854,855,858,859,862,863,866],{},"O ajuste que minimiza o erro quadrático ",[55,856,857],{},"antes"," de limiarizar não é o mesmo ajuste que minimiza o erro ",[55,860,861],{},"depois"," de limiarizar. São dois objetivos parecidos, mas não idênticos. O Bishop explica o motivo: mínimos quadrados corresponde a assumir que o ruído nos dados segue uma distribuição gaussiana (capítulo 3), o que faz todo sentido pra prever um número contínuo. Mas um rótulo binário não tem ruído gaussiano, e por isso mínimos quadrados aplicado direto em classificação sofre de um problema específico: pontos que já estão \"certos demais\", bem longe da fronteira, do lado certo, ainda ",[55,864,865],{},"puxam o ajuste",", porque contribuem erro quadrático mesmo sem precisar. O Bishop mostra isso com um exemplo onde adicionar pontos extras, todos do lado certo da fronteira, muda a fronteira de decisão pra pior, coisa que não acontece com um método pensado direito pra classificação (regressão logística, que a matéria ainda não chegou, mas que existe exatamente pra resolver essa mancada).",[21,868,870],{"id":869},"todas-as-30-variáveis","Todas as 30 variáveis",[11,872,873,874,877],{},"Com o mesmo ",[29,875,876],{},"LinearClassifier",", agora treinado nas 30 colunas em vez de 1:",[34,879,881],{"className":36,"code":880,"language":38,"meta":39,"style":39},"modelo = LinearClassifier()\nmodelo.fit(X_train, y_train)\nprint(accuracy_score(y_test, modelo.predict(X_test)))\n",[29,882,883,888,893],{"__ignoreMap":39},[43,884,885],{"class":45,"line":46},[43,886,887],{},"modelo = LinearClassifier()\n",[43,889,890],{"class":45,"line":138},[43,891,892],{},"modelo.fit(X_train, y_train)\n",[43,894,895],{"class":45,"line":144},[43,896,897],{},"print(accuracy_score(y_test, modelo.predict(X_test)))\n",[50,899,900],{},[11,901,902,904,905,908],{},[55,903,57],{}," acurácia de teste ",[55,906,907],{},"0.9561",", contra 0.8947 usando só uma variável.",[11,910,911,912,916],{},"O mesmo salto que eu já vi ",[15,913,915],{"href":914},"\u002Fplaylists\u002Fpattern-recognition\u002Flinear-regression-estimator","nos dois posts anteriores",": mais informação, sem mudar o algoritmo, sem mudar o limiar, e o resultado melhora bastante.",[21,918,920],{"id":919},"normalizando-mas-por-um-motivo-diferente","Normalizando, mas por um motivo diferente",[11,922,923,924,927,928,931,932,936],{},"A segunda metade da aula (",[29,925,926],{},"aula03b",") muda de assunto: em vez de regressão limiarizada, o professor usa o ",[29,929,930],{},"KNeighborsClassifier"," do scikit-learn (",[320,933,935],{"definition":934},"classifica um ponto novo olhando os K vizinhos mais parecidos (mais próximos) no conjunto de treino e votando pela classe mais comum entre eles, com aula própria mais na frente da matéria","KNN",", K vizinhos mais próximos, tema de uma aula só sua mais na frente, aqui ele só aparece de relance) direto no dataset de câncer, sem normalizar nada:",[34,938,940],{"className":36,"code":939,"language":38,"meta":39,"style":39},"modelo = KNeighborsClassifier()\nmodelo.fit(X_train, y_train)\nmodelo.score(X_test, y_test)\n",[29,941,942,947,951],{"__ignoreMap":39},[43,943,944],{"class":45,"line":46},[43,945,946],{},"modelo = KNeighborsClassifier()\n",[43,948,949],{"class":45,"line":138},[43,950,892],{},[43,952,953],{"class":45,"line":144},[43,954,955],{},"modelo.score(X_test, y_test)\n",[50,957,958],{},[11,959,960,962],{},[55,961,57],{}," 0.9298.",[11,964,965],{},"Depois ele normaliza (min-max, na faixa 0 a 1, calculado só com estatística do treino e aplicado igual no teste) e repete:",[50,967,968],{},[11,969,970,972],{},[55,971,57],{}," 0.9649.",[11,974,975,976,980,981,985],{},"E confere que padronizar (",[320,977,979],{"definition":978},"subtrair a média e dividir pelo desvio padrão, deixando cada variável centrada em zero com espalhamento 1","z-score",", a mesma normalização ",[15,982,984],{"href":983},"\u002Fplaylists\u002Fmachine-learning-specialization\u002Fw2-lab03-feature-scaling","da playlist da especialização",") dá o mesmo resultado: 0.9649 também.",[11,987,988,989,992,993,996,997,1000,1001,1004,1005,1008],{},"Eu já vi normalização importar antes, mas por um motivo diferente: ",[15,990,991],{"href":983},"lá na playlist da especialização",", normalizar ajudava o ",[55,994,995],{},"gradiente descendente"," a convergir mais rápido, porque a tigela de custo ficava menos alongada. Aqui o motivo é outro. KNN decide a classe olhando a ",[55,998,999],{},"distância"," entre pacientes, geralmente distância euclidiana, a raiz da soma dos quadrados das diferenças em cada variável. Se uma variável (tipo ",[29,1002,1003],{},"mean area",", que varia de 143 até 2501) tem uma escala centenas de vezes maior que outra (tipo ",[29,1006,1007],{},"mean smoothness",", que varia de 0.05 até 0.16), a distância acaba sendo decidida quase inteiramente pela variável de escala grande, as outras 29 praticamente não pesam na conta. Normalizar bota todo mundo na mesma régua antes de medir distância, e de repente as 30 variáveis contribuem de verdade, não só uma.",[21,1010,1012],{"id":1011},"fechando","Fechando",[1014,1015,1016,1030],"table",{},[1017,1018,1019],"thead",{},[1020,1021,1022,1027],"tr",{},[1023,1024,1026],"th",{"align":1025},"left","O que eu já sabia",[1023,1028,1029],{"align":1025},"O que essa aula assentou",[1031,1032,1033,1046,1054],"tbody",{},[1020,1034,1035,1039],{},[1036,1037,1038],"td",{"align":1025},"Regressão ajusta um número contínuo",[1036,1040,1041,1042,1045],{"align":1025},"Threshold (um ",[29,1043,1044],{},"> 0.5",") transforma esse número numa decisão binária, virando um classificador",[1020,1047,1048,1051],{},[1036,1049,1050],{"align":1025},"MSE e acurácia medem coisas diferentes",[1036,1052,1053],{"align":1025},"Pra rótulo binário limiarizado, elas são literalmente complementares: acurácia + MSE = 1",[1020,1055,1056,1059],{},[1036,1057,1058],{"align":1025},"Normalizar ajuda o gradiente descendente a convergir",[1036,1060,1061],{"align":1025},"Normalizar também ajuda qualquer método baseado em distância (como o KNN) a não deixar uma variável de escala grande dominar sozinha",[11,1063,1064],{},"E ficou um gancho puro pra próxima aula, que ainda não chegou nessa playlist: mínimos quadrados aplicado em classificação tem um viés estrutural (o Bishop mostrou o porquê), e o jeito certo de resolver isso é trocar a função de erro por uma pensada pra classificação, regressão logística.",[21,1066,1068],{"id":1067},"aplicação-prática","Aplicação Prática",[11,1070,1071,1072,1074],{},"Repito o classificador de uma variável (",[29,1073,61],{},") e visualizo, paciente por paciente no conjunto de teste, onde ele acerta e onde erra, junto com a fronteira de decisão real.",[34,1076,1078],{"className":36,"code":1077,"language":38,"meta":39,"style":39},"classifier = LinearClassifier()\nclassifier.fit(X_train, y_train)  # 1 variável: worst concave points\ny_pred_test = classifier.predict(X_test)\n",[29,1079,1080,1085,1090],{"__ignoreMap":39},[43,1081,1082],{"class":45,"line":46},[43,1083,1084],{},"classifier = LinearClassifier()\n",[43,1086,1087],{"class":45,"line":138},[43,1088,1089],{},"classifier.fit(X_train, y_train)  # 1 variável: worst concave points\n",[43,1091,1092],{"class":45,"line":144},[43,1093,1094],{},"y_pred_test = classifier.predict(X_test)\n",[11,1096,1097,1098,1101,1102,1105,1106,1109],{},"Refiz esse ajuste com ",[29,1099,1100],{},"random_state=42"," (o notebook original não fixa a semente, então os números variam a cada execução, aqui uso um treino\u002Fteste fixo pra poder mostrar os pacientes exatos) e obtive ",[29,1103,1104],{},"w_ = [-0.296, 5.841]",", o que coloca a fronteira de decisão em ",[29,1107,1108],{},"worst concave points ≈ 0.136",": abaixo disso o modelo prevê benigno, acima, maligno.",[1111,1112],"classification-fit-scatter",{":actual":1113,":correct":1114,":threshold":1115,":x":1116,"class0-label":1117,"class1-label":1118,"correct-label":1119,"threshold-label":1120,"wrong-label":1121,"x-label":61},"[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]","[1, 1, 1, 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, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]","0.1362","[0.0, 0.0, 0.0, 0.0, 0.0223, 0.032, 0.0341, 0.0431, 0.0459, 0.0481, 0.0533, 0.0556, 0.0558, 0.0622, 0.0627, 0.0641, 0.0737, 0.0763, 0.0783, 0.0791, 0.0796, 0.0828, 0.0831, 0.0875, 0.0895, 0.0917, 0.0961, 0.0998, 0.101, 0.1015, 0.1015, 0.1017, 0.1045, 0.1047, 0.1092, 0.1105, 0.1136, 0.118, 0.1225, 0.1312, 0.1427, 0.1456, 0.1474, 0.1659, 0.1673, 0.1739, 0.1789, 0.1841, 0.1848, 0.1932, 0.1974, 0.2013, 0.2148, 0.221, 0.2264, 0.2422, 0.243, 0.2508, 0.265, 0.2867]","Benigno","Maligno","Acertou","Fronteira de decisão","Errou",[11,1123,1124,1125,1127],{},"Os triângulos vermelhos (os erros) se concentram bem perto da fronteira pontilhada, dos dois lados, exatamente onde eu esperaria: são os casos ambíguos, onde ",[29,1126,61],{}," sozinho não separa bem maligno de benigno. Longe da fronteira, dos dois lados, o modelo acerta quase sempre. Faz sentido: uma variável só carrega bastante sinal (ela tinha 0.79 de correlação, afinal), mas não é suficiente pra nunca errar, e é exatamente por isso que as 30 variáveis juntas (0.9561) batem a variável sozinha (0.8947).",[1129,1130,1131],"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":39,"searchDepth":138,"depth":138,"links":1133},[1134,1135,1136,1137,1138,1139,1140,1141,1142,1143],{"id":23,"depth":138,"text":24},{"id":79,"depth":138,"text":80},{"id":114,"depth":138,"text":115},{"id":238,"depth":138,"text":239},{"id":743,"depth":138,"text":744},{"id":792,"depth":138,"text":793},{"id":869,"depth":138,"text":870},{"id":919,"depth":138,"text":920},{"id":1011,"depth":138,"text":1012},{"id":1067,"depth":138,"text":1068},null,"2026-08-19","Aula 3a e 3b: o professor reaproveita a mesma classe de regressão linear pra classificar câncer de mama, mostra por que faltava um limiar, e depois normaliza os dados por um motivo diferente de tudo que eu já tinha visto.","md",{},"\u002Fpt\u002Fplaylists\u002Fpattern-recognition\u002Fclassification-threshold","pattern-recognition",{"title":6,"description":1146},"published","pt\u002Fplaylists\u002Fpattern-recognition\u002Fclassification-threshold",[1155,1156,1157],"classificacao","normalizacao","knn","zexb9kLmS_F0d2-k22F7rLKZW98HgfwPVOA1qHE5A_M",1787338983193]