·MVC는 사용자 인터페이스나 데이터 및 논리 제어를 구현하는데 널리 사용되는 소프트웨어 디자인 패턴
-M(Model): 데이터와 비즈니스 로직을 관리
-V(View): 레이아웃과 화면을 처리
-C(Controller): 명령을 모델과 뷰 부분으로 라우팅, 앱 사용자의 입력에 대한 응답으로 모델과 뷰를 업데이트
·app.js에서 '여기는 루트입니다.'화면과 로그인 화면을 작성한 부분을 따로 분리해주기
1) app.set 설정
// 화면 뷰를 저장하고 관리해줄 파일을 ./views로 지정
app.set('views', './views');
// views 폴더 안에 생성될 html코드들을 어떤 엔진으로 해석할지 결정('ejs', 그냥 html이랑 비슷)
app.set('view engine', 'ejs');
2) res.send 내에 적어놨던 html 코드를 ejs 파일로 각각 분리
-작업하던 폴더 > views > home 폴더를 생성
-home 폴더 내에 index.ejs, login.ejs 파일을 생성하여 각각 '이곳은 루트입니다.'와 로그인화면의 html 코드를 붙여넣기
3) app.js파일에서 원래 res.send로 넣어놨던 html코드는 지우고 ejs파일을 받아줄 코드 작성
app.get('/',(req, res)=>{
// ejs파일이 이 파일 내에 들어왔을 때 이동할 곳 지정
// app.set에서 views 파일은 지정해놓았으므로 views 폴더 내의 경로만 지정하면 됨
res.render('home/index');
});
app.get('/login', (req,res)=>{
res.render('home/login');
});
1) express라는 작업 폴더 생성 후 자바스크립트 파일 하나 생성(서버의 중심 파일)
"use strict"
// require를 사용하여 express 모듈 다운
const express = require('express')
// app 변수에 express를 실행시켜 넣어주기
const app = express();
// PORT 번호 설정, PORT는 배가 들어가는 항구에 여러 선착장이 있듯 서버에 존재하는 여러 개의 장소
const PORT = 3000;
// listen 명령어로 서버 띄우기(3000번 포트로 열기)
app.listen(PORT, ()=>{
console.log('서버 가동') // 서버 콘솔에는 간단하게 '서버 가동'이라는 문장 띄우며 서버 오픈
});
2) express라는 서버 프레임워크 사용
3) npm(node package manager)을 통해 express 모듈 설치
- 터미널에 'npm init' 입력 후 'Is this OK? (yes)' 나올 때까지 엔터(전부 기본값으로 설치)
- 설치 완료후 터미널에 'npm install express --save' 입력
- 설치 완료하면 작업 폴더에 node_modules폴더와 package.json(npm으로 설치한 모듈들 관리해주는 메모장), package-lock.json(모듈에 대해 더 자세히 적힌 메모장)파일이 생성됨
- 그러면 node 명령어를 통해 서버 가동 준비 완료
4) 터미널에 node 명령어는 자바스크립트 파일을 v8엔진을 사용해 코드를 해석해달라는 명령어
- 터미널에 'node app.js' 입력하면 '서버 가동'이 출력되며 서버 오픈
- 주소창에 'localhost:3000' 입력하면 열린 서버창 오픈
- 윈도우에 'Cannot Get /'은 루트 경로를 찾을 수 없다는 의미
5) 루트 만들어주기
// 위에서 작성한 코드에서 app.listen 전에 아래의 코드 삽입
// 브라우저에서 '/'루트로 요청이 들어오면
// req 변수로 요청을 받고(어떤 요청인지는 아직 해석 x)
// res 변수로 어떤 응답을 할 지 결정('여기는 루트입니다'라는 문장 보내기로 응답)
app.get('/', (req, res) => {
res.send('여기는 루트입니다.');
});
// 브라우저에서 '/login'루트로 요청이 들어오면
// req 변수로 요청을 받고(어떤 요청인지는 아직 해석 x)
// res 변수로 어떤 응답을 할 지 결정('여기는 로그인 화면입니다'라는 문장 보내기로 응답)
app.get('/login', (req, res) => {
res.send('여기는 로그인 화면입니다.');
});
<!DOCTYPE>
<html lang="ko"> //문서 기본 언어
<head>
<title>제목</title> // 문서의 제목(윈도우창 제목)
<meta charset="UTF-8"> // 문서 인코딩 정보
<meta name="author" content="저자이름"> // 문서 작성자
<meta name="description" option="설명"> // 문서 설명
<link rel="stylesheet" href="my-css-file.css"> // 문서 스타일 적용할 css파일
<style>
... // css파일 외에 직접 설정할 스타일
</style>
...
</head>
<body>
...
</body>
<script src="my-js-file.js"></script> // JS파일을 직접 설정하여 적용 또는 script내에 써서 적용
<footer>
... // 문서 맨 아래에 쓸 정보
</footer>
</html>
2. Body
// 01. <h>
<h1>메인제목</h1>
<h2>소제목</h2>
<h3>소제목의 소제목</h3>
...
<h6>제일 작은 제목</h6>
// 글씨 크기가 점점 작아짐
// 03. <span>
<span style="font-size:32px, margin:21px">문장</span>
// <h>와 <p>는 각각 제목, 단락이라는 의미가 있지만
// span은 "이게 뭐다"라는 의미는 없음, 스타일 설정으로 제목이나 그냥 문장처럼 보일 수 있게 함
// 04. <br>
//한줄 띄우기
// 05. <ul> 또는 <ol>
<ul>
<li>요소1</li>
<li>요소2</li>
<li>...</li>
<li>요소n</li>
</ul>
// ul: 순서가 없는 리스트(글머리 표시가 ○, ■ 등 일반 기호)
// ol: 순서가 있는 리스트(글머리 표시가 1,2,3 또는 a,b,c 등 순서가 있는 문자)
// 07. 하이퍼링크
<a href="하이퍼링크 할 주소" title="단어 위에 마우스 오버시 툴팁 표시">누르면 하이퍼링크 될 단어</a>
// 08. Description List
<dl>
<dt>용어, 질문 등 상위 항목</dt>
<dd>정의, 답변 등 하위 항목</dd>
</dl>
// <dd></dd>내의 내용은 들여쓰기 됨
// 09. 인용문
<blockquote cite="인용할 링크">
<p>인용문</p>
</blockquote>
// 왼쪽에 굵은 선과 함께 인용문이 한 단락으로 표시
<p>...단락...<q cite="인용할 링크">인용문</q>...단락...</p>
// 단락 내에 인용문이 삽입
<p>...단락...<a href="인용할 링크"><cite>인용문</cite></a>...단락...</p>
// 단락 내에 인용문이 Italic체로 표시, 클릭 시 하이퍼링크
// 10. 약어
<p>...단락...<abbr title="약어의 전체 의미">약어</abbr>...단락...</p>
// 약어로 표시되고 마우스 오버시 전체 의미 표시
// 11. 연락처 세부정보
<address>
<p>...단락...<a href="연락처 등 하이퍼링크">문서작성자</a>...단락...</p>
</address>
// 문서 작성자와 연결할 수 있는 링크 삽입
// 13. 컴퓨터 코드
<code></code> // 일반적인 코드 표시
<pre>< // 공백(들여쓰기) 유지
<var></var> // 변수 이름 특별하게 표시
<kbd></kbd> // 컴퓨터에 입력된 키보드(및 기타유형) 입력 표시
<samp></samp> // 컴퓨터 프로그램의 출력 표시
1) <code> 결과:
이렇게 됨
2) <pre> 결과:
이렇게 됨
3) <var> 결과:
<var> 사용 =>
ABC (조금 기울어짐)
<var> 사용 안함 => ABC ( 안기울어짐)
4) <kbd> 결과:
그냥 => Ctrl
kbd =>
Ctrl
5) <samp> 결과:
그냥 =>
Keyboard not found Press F1 to continue
samp =>
Keyboard not found Press F1 to continue
// 14. 시간 및 날짜
<time datetime="컴퓨터에게 읽힐 시간">사람이 읽는 시간(화면에 표시되는 시간</time>
·2016-01-20
·2016-01
·01-20
·19:30
·19:30:01.856
·2016-01-20 T 19:30+01:00
·2016-w04(2016년 4주차)
// 15. Body의 구성
<header></header> // 컴텐츠 소개, 제목
<nav></nav> // 다른 페이지로 이동하기 위한 함수적 네비게이션
<main></main> // 페이지의 독자적 컨텐츠, body에서 딱 한번 사용
<article></article> // 개별적으로 의미를 가지는 컨텐츠
<section></section> // 개별 컨텐츠(<article>) 그룹화
<aside></aside> // 메인 컨텐츠와 직접 연관x, 간접적으로 도움이 되는 추가 정보
<footer></footer> // 마지막 컨텐츠 그룹
<hr> // 수직선, 주제 변경시 사용
// 16. 이미지 삽입
<img src="이미지 경로" // 이미지 로드 오류시 이미지 대신 출력
alt="이미지 이름, 설명"
width="폭" height="높이"
title="이미지 이름, 설명" // 이미지 위에 마우스 오버 시 툴팁 표시
<figcaption>이미지 캡션</figcaption> //<p>와 똑같이 표시되지만 컴퓨터에게 이미지 캡션임을 알리기 위해 사용
// 17. 비디오 삽입
<video src="비디오 경로" controls> // controls는 비디오 컨트롤(재생, 일시정지, 볼륨 조절) 기능 추가
// +적절한 JS api를 이용해 기능 추가 가능
</video>
// +추가로 이용 가능한 속성
<video autoplay> // 로드 시 자동재생
<video loop> // 반복재생
<video muted> // 음소거
<video preload="auto"> // 페이지 로드 시 비디오 전체 로드
<video preload="metadata"> // 페이지 로드 시 비디오 메타데이터 로드
<video preload="none"> // 페이지 로드 시 비디오 로드x
<source src="비디오 경로1" type="비디오 타입1">
<source src="비디오 경로2" type="비디오 타입2">
// <video>안에 src 옵션없이 따로 넣어서 비디오 타입이 호환되지 않을 것을 대비
<track kind="subtitles" src="자막 파일(.vtt 형식) srclang="자막언어*(ko, en 등) label="표시할 자막 이름">
// 자막 표시
// 18. 오디오 삽입
비디오 삽입에서 <video> 대신 <audio> 넣으면 됨
// 19. iframe
<iframe src="iframe 주소" width="폭" height="높이"
"frameborder="프레임 경계" style="스타일" allowfullscreen>
</iframe>
//유튜브, 구글맵 등에서 iframe 주소를 복사해 삽입 가능
-iframe은 해킹의 위험이 존재
-> 필요할 때만 사용
-> HTTPS 사용(HTTP의 암호화된 버전)
-> sandbox 속성 사용(가능한 모든 제한을 줌)
=> 일부 기능을 허용하고 싶다면 sandbox=" " 따옴표 안에 기능 넣기
=> allow-scripts, allow-same-origin옵션은 넣으면 sanbox를 끌 수 있으므로 넣지 말기
->CSP설정(Content Security Policy)
// 20. <svg>
<svg version="1.1" baseProfile="full" width="폭" height="높이" xmlns="XML namespace">
// XMLnamespace는 svg가 처음 정의된 xml 주소
<rect width="사각형 폭" height="사각형 넓이" fill="사각형 색깔" />
<circle cx="원 중심의 x좌표" cy="원 중심의 y좌표" r="반지름" fill="원 색깔" />
</svg>
// svg로 사각형, 원 등 간단한 도형은 쉽게 코딩할 수 있지만 복잡한 도형은 코딩하기 어려움
// svg파일이 있다면
<img src="svg 파일 경로" alt="이미지 설명" height="그림 높이" width="그림 폭" />
// JS나 css로 조정 불가능, css사용하려면 svg 내용에 인라인으로 구문 추가
// svg 지원 안하는 웹사이트에 대해
<img src=".png로 바꾼 경로" alt="이미지 설명" srcset=".svg로 된 경로" />
// css의 백그라운드로 svg이지미 지정, 브라우저가 지원 안 할것을 대비해 두 개 지정
background: url(".png파일") no-repeat center;
background-image: url(".svg 파일");
background-size: contain;
// <td> 대신 <th> 쓰면 표의 글씨가 굵어지고 표의 각 행렬의 제목(header)을 표시
// <td> </td>는 빈 셀을 생성
// <td colspan="합칠 열의 개수">로 열 병합
// <td rowspan="합칠 행의 개수">로 행 병합
<table>
<colgroup>
<col style="스타일" span="적용할 열" />
</colgroup>
<caption>
표의 캡션
</caption>
<thead>
표의 첫 행(굵게 표시)
</thead>
<tbody>
일반적인 표 구성(tfoot보다 밑에 구성될 수도 있음)
</tbody>
<tfoot>
표의 마지막 행(합계 등 표시)
</tfoot>
<table>...</table> // 표 안에 표 넣기 가능
</table>
// <th scope="header 열" 또는 "header 행">으로 화면리더기가 해당 셀이 header임을 인식하게 할 수 있음
// <th id="th를 식별할 id">
// <td headers="열_header_id 행_header_id> // 윗줄에서 th에 id를 부여한 것과 연계하여 td가 어떤 th에 속해있는지 연결
-선형 회귀 모형에서 비용함수(RMSE 또는 MSE 등)을 최소화하는 회귀 계수 θ를 찾기 위한 해석적인 방법
-공식 검증
import numpy as np
import matplotlib.pyplot as plt
# 선형관계에 있는 변수 X, y 100개씩 100행 1열의 배열로 생성
X=2*np.random.rand(100,1)
y=4+3*X+np.random.randn(100,1)
plt.plot(X,y,'b.')
plt.axis([0,2,0,15])
plt.show()
X_b=np.c_[np.ones((100,1)),X] # 모든 샘플에 X0=1을 추가
# X0에 1을 추가한 독립변수 배열에 배열을 전치시킨 것을 곱하여
# 그 역행렬을 구하고
# 역행렬과 독립변수 배열을 전치시킨 배열을 곱한 뒤
# 종속변수 행렬 곱하기
theta_best=np.linalg.inv(X_b.T.dot(X_b)).dot(X_b.T).dot(y)
theta_best
### 결과 ###
array([[4.1140573 ],
[2.81512351]])
-엑셀을 이용해 theta_best=np.linalg.inv(X_b.T.dot(X_b)).dot(X_b.T).dot(y) 계산과정 설명
1) 이해를 위해 X와 y 변수에서 각각 세개씩만 데이터 추출한 뒤 X_b, y 배열 생성
6) np.linalg.inv(X_b.T.dot(X_b)).dot(X_b.T) : 5)에서 구한 역행렬과 3)에서 전치시켰던 행렬 곱하기
-"=MMULT(행렬, 행렬)" 식 작성 후 Ctrl+Shift+Enter
7) np.linalg.inv(X_b.T.dot(X_b)).dot(X_b.T).dot(y) : 마지막 y 행렬 곱하기
-"=MMULT(행렬, 행렬)" 식 작성 후 Ctrl+Shift+Enter
8) 결과
-파이썬과 비교해보면 똑같은 값이 나옴
-위와 같은 과정을 거쳐서 나온 theta_best를 이용하여 새로운 값에 대한 y값 예측하기
# 새로운 값을 예측
# 0과 2를 모델에 넣었을 때 각각의 예측값
X_new=np.array([[0],[2]])
X_new_b=np.c_[np.ones((2,1)),X_new]
y_predict=X_new_b.dot(theta_best)
y_predict
### 결과 ###
array([[4.25816196],
[9.8437471 ]])
-예측한 0과 2의 값으로 예측된 회귀직선 그려보기
# 그래프로 표시
plt.plot(X_new,y_predict,'r-')
plt.plot(X,y,'b.')
plt.axis([0,2,0,15])
plt.show()
-사이킷런에서 선형회귀
# 사이킷런에서 선형 회귀 모형
from sklearn.linear_model import LinearRegression
# 선형 회귀 모형 객체 생성
lin_reg=LinearRegression()
# X와 y를 모형에 피팅
lin_reg.fit(X,y)
# 선형회귀 모형의 절편과 회귀계수
lin_reg.intercept_, lin_reg.coef_
### 결과 ###
(array([4.25816196]), array([[2.79279257]]))
# 새로운 값(0,2)에 대한 예측
lin_reg.predict(X_new)
### 결과 ###
array([[4.25816196],
[9.8437471 ]])
-LinearRegression 클래스는 scipy.linalg,lstsq() 함수를 기반으로 함
-함수를 직접 호출하면 sklearn의 LinearRegressor()에서 구했던 절편과 회귀계수와 같은 값이 출력
# scipy.linalg.lstsq() 함수
theta_best_svd,residuals,rank,s=np.linalg.lstsq(X_b,y,rcond=1e-6)
theta_best_svd
### 결과 ###
array([[4.25816196],
[2.79279257]])
-scipy.linalg.lstsq() 함수는 다음 식을 계산함 $$\hat{\theta }=X^+y$$
-X+는 X의 유사역행렬
-유사역행렬은 특잇값 분해(SVD)라 부르는 표준 행렬 분해 기법을 사용해 계산
2. 계산 복잡도
-정규 방정식은 (n+1)*(n+1) 크기가 되는 $$X^TX$$의 역행렬 계산
-역행렬을 계산하는 계산 복잡도는 특성 수가 n배로 늘어나면 계산시간이 n^2.4에서 n^3 사이로 늘어남
-사이킷런의 LinearRegression 클래스가 사용하는 SVD 방법은 약 n^2로 특성 수가 두 배 늘어나면 계산시간은 대략 4배 늘어남
-정규방정식이나 다른 알고리즘으로 학습된 선형 회귀모델은 예측이 매우 빠름
(예측 계산 복잡도는 샘플 수와 특성 수에 선형적으로 샘플(또는 특성)이 두배 증가하면 걸리는 시간도 거의 두배 증가)
# 사이킷런에서 기본적으로 제공하는 MNIST 데이터셋 불러오기
from sklearn.datasets import fetch_openml
mnist=fetch_openml('mnist_784',version=1,as_frame=False)
mnist.keys()
### 결과 ###
dict_keys(['data', 'target', 'frame', 'categories', 'feature_names', 'target_names', 'DESCR', 'details', 'url'])
-MNIST 데이터셋 배열 확인
X,y=mnist['data'],mnist['target']
X.shape # (70000, 784)
y.shape # (70000,)
# 이미지가 70000개이고 각 이미지는 784개의 특성을 가짐(28*28 픽셀의 이미지이므로 28*28=784개의 특성을 가짐)
# 각 특성은 0(흰색)~255(검은색)까지의 픽셀 강도
2. 다중 분류기 훈련
-서포트 벡터 머신(SVC) 분류기 사용
from sklearn.svm import SVC
svm_clf=SVC()
svm_clf.fit(X_train, y_train)
svm_clf.predict([some_digit])
### 결과 ###
array([5], dtype=uint8)
# 이진 분류기에서 5인지 아닌지에 따라 True, False로 결과가 나온 것과 달리 0~9까지 숫자 중 5라고 분류해냄
-OvO(OneVsOneClassifier): 0과1 구별, 1과 2 구별..과 같이각 숫자 조합마다 이진 분류를 통해 분류기를 훈련시킴(N*(N-1)/2 개의 분류기 필요)
-OvR(OneVsRestClassifier): 모든 숫자를 훈련시킨 후, 가장 점수가 높은 것을 선택
-서포트 벡터 머신 같은 일부 알고리즘에서는 큰 훈련세트에서 몇 개의 분류기를 훈련시키는 것보다는 작은 훈련세트에서 많은 분류기 훈련시키는 것 선호
-이진 분류 알고리즘에서는 대부분 OvR 선호
-OvO나 OvR 강제로 사용하기
# OvO나 OvR 사용을 강제하려면 OneVsOneClassifier나 OneVsRestClassifier 사용
from sklearn.multiclass import OneVsRestClassifier
ovr_clf=OneVsRestClassifier(SVC())
ovr_clf.fit(X_train, y_train)
ovr_clf.predict([some_digit])
### 결과 ###
array([5], dtype=uint8)
# SGD 분류기는 직접 샘플을 다중 클래스로 분류할 수 있으므로 별도로 OvO 또는 OvR 적용할 필요 없음
# SGDClassifier 훈련
from sklearn.linear_model import SGDClassifier
sgd_clf=SGDClassifier(random_state=42)
sgd_clf.fit(X_train, y_train)
sgd_clf.predict([some_digit])
### 결과 ###
array([3], dtype=uint8)
sgd_clf.decision_function([some_digit])
### 결과 ###
array([[-31893.03095419, -34419.69069632, -9530.63950739,
1823.73154031, -22320.14822878, -1385.80478895,
-26188.91070951, -16147.51323997, -4604.35491274,
-12050.767298 ]])
-SGDClassifier에서 5를 3으로 분류해버림
-클래스마다 부여한 점수확인 결과 3에 부여한 점수가 1823으로 가장 높고 5에 부여한 점수는 그 다음으로 높은 -1385
# SGDClassifier의 성능 평가
from sklearn.model_selection import cross_val_score
cross_val_score(sgd_clf, X_train, y_train, cv=3, scoring='accuracy') # array([0.87365, 0.85835, 0.8689 ])
# 스케일 조정을 하면 정확도를 높일 수 있음
from sklearn.preprocessing import StandardScaler
scaler=StandardScaler()
X_train_scaled=scaler.fit_transform(X_train.astype(np.float64))
cross_val_score(sgd_clf,X_train_scaled,y_train,cv=3,scoring='accuracy') # array([0.8983, 0.891 , 0.9018])
3. 에러 분석
-모델의 성능을 향상시키기 위해 에러의 종류를 분석하여 확인하는 것
from sklearn.model_selection import cross_val_predict
from sklearn.metrics import confusion_matrix
y_train_pred=cross_val_predict(sgd_clf, X_train_scaled, y_train, cv=3)
conf_mx=confusion_matrix(y_train, y_train_pred)
conf_mx
# 첫 행부터 실제 0일 때 0으로 예측한 개수, 1로 예측한 개수, 2로 예측한 개수...
-각 숫자를 정확히 예측한 개수가 가장 많지만 숫자 5에 대해 5로 예측한 횟수는 4444로 다른 숫자에 비해 낮음
-시각화 해보기
import matplotlib.pyplot as plt
plt.matshow(conf_mx, cmap=plt.cm.gray)
plt.show()
-숫자 5부분만 조금 진한색으로 표시되어 다른 숫자들보다 정확히 예측해낸 횟수가 적음을 의미
-에러의 개수가 아닌 비율을 시각화해보기
-단순히 5의 전체 개수가 적어서 생긴 현상일 수 있으므로 전체 개수 대비 정확히 예측한 비율을 시각화
# 각 행의 합계 계산
row_sums=conf_mx.sum(axis=1,keepdims=True)
# 각 값을 행의 전체 합계로 나누어 비율 확인
norm_conf_mx=conf_mx / row_sums
# 대각원소는 0으로 채워 무시하고 나머지 값에서 에러 비율의 크기 확인
np.fill_diagonal(norm_conf_mx,0)
plt.matshow(norm_conf_mx, cmap=plt.cm.gray)
plt.show()
-8열이 밝은 것으로 보아 많은 이미지가 8로 잘못 분류됨
-3행 5열과 5행 3열이 밝은 것은 3과 5를 서로 잘못 분류한 비율이 높음을 의미
-3과 5에 대해 3을 3으로, 3을 5로, 5를 3으로, 5를 5로 예측한 데이터들을 한번에 살펴보기
# 그림 그리기 기능 함수
import matplotlib as mpl
def plot_digit(data):
image=data.reshape(28,28)
plt.imshow(image,cmap=mpl.cm.binary,interpolation='nearest')
plt.axis('off')
def plot_digits(instances, images_per_row, **options):
size=28
images_per_row=min(len(instances),images_per_row)
n_rows=(len(instances)-1) // images_per_row +1
# 필요하면 그리드의 끝을 채우기 위해 빈 이미지 추가
n_empty=n_rows*images_per_row-len(instances)
padded_instances=np.concatenate([instances, np.zeros((n_empty, size*size))],axis=0)
# 배열의 크기를 바꿔 28*28 이미지를 담은 그리드로 구성
image_grid=padded_instances.reshape((n_rows, images_per_row, size, size))
# 축 0(이미지 그리드의 수직축)과 2(이미지의 수직축)를 합치고 축 1과 3(그리드와 이미지의 수평축)을 합침
# transpose()를 통해 결합하려는 축을 옆으로 이동한 다음 합침
big_image=image_grid.transpose(0, 2, 1, 3).reshape(n_rows*size,images_per_row*size)
# 하나의 큰 이미지 출력
plt.imshow(big_image, cmap=mpl.cm.binary, **options)
plt.axis('off')
# 개개의 오류 살펴보며 왜 잘못되었는지 생각해보기(3과 5를 예시로)
cl_a, cl_b=3,5
X_aa=X_train[(y_train==cl_a) & (y_train_pred==cl_a)] # 실제 3을 3으로 예측
X_ab=X_train[(y_train==cl_a) & (y_train_pred==cl_b)] # 실제 3을 5로 예측
X_ba=X_train[(y_train==cl_b) & (y_train_pred==cl_a)] # 실제 5를 3으로 예측
X_bb=X_train[(y_train==cl_b) & (y_train_pred==cl_b)] # 실제 5를 5로 예측
plt.figure(figsize=(8,8))
plt.subplot(221); plot_digits(X_aa[:25], images_per_row=5) # X_aa에 해당하는 데이터를 처음 25개만 불러와서 5행으로 정렬
plt.subplot(222); plot_digits(X_ab[:25], images_per_row=5) # X_ab에 해당하는 데이터를 처음 25개만 불러와서 5행으로 정렬
plt.subplot(223); plot_digits(X_ba[:25], images_per_row=5) # X_ba에 해당하는 데이터를 처음 25개만 불러와서 5행으로 정렬
plt.subplot(224); plot_digits(X_bb[:25], images_per_row=5) # X_bb에 해당하는 데이터를 처음 25개만 불러와서 5행으로 정렬
plt.show()
-5를 3으로 잘못 예측해낸 것(제3사분면) 중 첫 행 2열은 사람이 봐도 3같을 정도로 잘못 분류할 확률이 높아보임
-위 방식으로 에러를 확인하여 어디서, 왜 오차가 나는지 확인하고 해결방법 고안하기
ex) 3과 5는 위의 선분과 아래의 원을 잇는 수직선의 위치가 왼쪽, 오른쪽으로 다르다는 점 등을 이용하여 다시 학습시키기
4. 다중 레이블 분류
-분류해내야 하는 타겟변수가 여러 개일 때, 여러 개를 한 번에 분류
-KNeighborsClassifier, DecisionTreeClassifier, RandomForestClassifier, OneVsRestClassifier에서 다중 분류 지원
from sklearn.neighbors import KNeighborsClassifier
y_train_large=(y_train>=7) # 분류한 결과가 7보다 큰지
y_train_odd=(y_train%2==1) # 분류한 결과가 홀수인지
y_multilabel=np.c_[y_train_large,y_train_odd] # 위의 두 개의 사항에 대해 예측하는 다중 레이블
knn_clf=KNeighborsClassifier()
knn_clf.fit(X_train,y_multilabel)
knn_clf.predict([some_digit]) # 숫자 5에 대해 예측결과 반환
### 결과 ###
array([[False, True]])
# f1_score를 통해 얼마나 정확한지 확인
from sklearn.metrics import f1_score
y_train_knn_pred=cross_val_predict(knn_clf,X_train,y_multilabel,cv=3)
f1_score(y_multilabel, y_train_knn_pred,average='macro')
### 결과 ###
0.976410265560605
-숫자 5를 7보다 크지 않고,(False), 홀수(True)라고 정확히 분류해냄
5. 다중 출력 분류
-다중 레이블 분류에서 한 레이블이 값을 두 개이상 가질 수 있는 분류
-MNIST 숫자 이미지 데이터는 한 픽셀당 한 레이블이므로 레이블이 784개인 다중 레이블임