·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');
});

 

 4) 서버 가동 시 ejs 모듈을 찾을 수 없다는 에러가 뜨므로 ejs 모듈 설치

 

   -모듈 설치는 터미널에 npm install ejs -s

 

 5) 결과: 각 루트 별 화면의 html코드를 분리하였고 서버 화면은 원래와 똑같이 구현

'back-end > Javascript' 카테고리의 다른 글

[Node.js] Controller 분리  (0) 2022.11.22
[Node.js] 라우팅 분리  (0) 2022.11.21
[Node.js] 서버에 로그인 화면 적용시키기  (0) 2022.11.20
[Node.js] https 서버 띄우기  (0) 2022.11.18
[Node.js] express 서버 띄우기  (0) 2022.11.18

다음 글에서 만든 로그인 화면 html파일을 express로 가동시키는 서버에 띄우기

 

[HTML] 로그인 화면 만들기

1) HTML 기본 틀 작성 2) 내부에 로그인 폼 작성 아이디 비밀번호 회원가입 3) 결과 4) 전체 코드 아이디 비밀번호 회원가입

data-science-study.tistory.com

 

"use strict"

const express = require('express');
const app = express();

app.get('/', (req, res) => {
  // ``안에 html 코드 내용을 그대로 복붙('' 또는 ""가 아닌 ``사용해야 html 코드 내의 ''와 ""가 인식되어 오류 안남
  res.send(`
    <!DOCTYPE html>
    <html lang="ko">
      <head>
        <meta charset="UTF-8">
        <title>로그인</title>
        <style>
          ul {list-style:none;}
        </style>
      </head>
      <body>
        '여기는 루트입니다.'
      </body>
    </html>
  `);
});

app.get('login', (req, res) => {
  res.send(`
    <!DOCTYPE html>
    <html lang='ko'>
      <head>
        <meta charset='UTF-8'>
        <title>로그인</title>
      </head>
      <body>
        <form id="login">
          <ul>
            <li>
              <label for="id">아이디</label>
              <input type="text" id="id">
            </li>
            <li>
              <label for="pw">비밀번호</label>
              <input type="password" id="pw">
            </li>
            <input type="submit" value="로그인">
            <button onclick="location.href='sign.html'">회원가입</button>
          </ul>
        </form>
      </body>
      </head>
    </html>
  `);
});

app.listen(3000, () => {
  console.log('서버 가동');
});

 

서버를 가동시키고 주소창에 localhost:3000/login을 입력하여 /login 루트를 찾아가면 html로 작성한 로그인 화면이 뜸

'back-end > Javascript' 카테고리의 다른 글

[Node.js] Controller 분리  (0) 2022.11.22
[Node.js] 라우팅 분리  (0) 2022.11.21
[Node.js] View 분리  (0) 2022.11.20
[Node.js] https 서버 띄우기  (0) 2022.11.18
[Node.js] express 서버 띄우기  (0) 2022.11.18

1) HTML 기본 틀 작성

<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="UTF-8">
    <title>로그인</title>
  </head>
  <body>
  </body>
</html>

 

2) <body> 내부에 로그인 폼 작성

<body>
    <form id="login">
      <ul>
        <li>
          <label for="id">아이디</label>
          <input type="text" id="id">
        </li>
        <li>
          <label for="pw">비밀번호</label>
          <input type="password" id="pw">
        </li>
        <input type="submit" value="로그인">
        <button onclick="location.href='sign.html'">회원가입</button>
      </ul>
    </form>
</body>

 

3) 결과

 

4) 전체 코드

<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="UTF-8">
    <title>로그인</title>
    <style>
      ul {list-style:none;}
    </style>
  </head>
  <body>
    <form id="login">
      <ul>
        <li>
          <label for="id">아이디</label>
          <input type="text" id="id">
        </li>
        <li>
          <label for="pw">비밀번호</label>
          <input type="password" id="pw">
        </li>
        <input type="submit" value="로그인">
        <button onclick="location.href='sign.html'">회원가입</button>
      </ul>
    </form>
  </body>
</html>

'Front-end > HTML' 카테고리의 다른 글

[HTML] 기본 틀  (0) 2022.11.15

express 서버 프레임워크를 사용하지 않고 개발하는 방법

express와 같이 자바스크립트 파일에서 작성

const http = require('http');

// http에서는 createServer 함수에 req, res를 파라미터로 받는 콜백함수를 생성
const app = createServer((req, res) => {
  console.log(req.url); // 서버에서 접속하는 경로를 console.log에 출력
});

app.listen(3001, () => {
  console.log('http로 가동된 서버');
});

  - 터미널에 'node app.js' 입력하여 서버 가동

   - 주소창에 'localhost:3001'을 입력하면 위에 로딩되는 것이 뜨면 서버가 열린 것

  - req.url을 console.log에 출력하도록 했으므로 처음 localhost에 들어가면 가장 첫 루트인 '/'가 console에 출력됨

  - 이후 'localhost:3001/login'을 주소창에 입력하면 /login의 경로 출력

 

  - req.url이 서버의 각 루트 경로를 가져오는 것을 이용하여 각 경로의 화면을 설정할 수 있음

// console.log(req.url); 대신 아래의 코드 입력

// 브라우저에서 한글(utf-8)로 응답해야한다고 알려주기
res.writeHead(200, {'Content-Type': 'text/html; charset=utf-8'});

if (req.url === '/'){
  res.end('이곳은 루트입니다.'); // express에서는 res.send()였지만 http에서는 res.end()로 사용
}
else if (req.url === '/login'){
  res.end('이곳은 로그인 화면입니다.');
}

 

  - express와 다르게 if문으로 각 경로에 대해 응답을 작성해야하므로 지저분해짐

  - 따라서 express를 쓰는 것이 훨씬 편리

'back-end > Javascript' 카테고리의 다른 글

[Node.js] Controller 분리  (0) 2022.11.22
[Node.js] 라우팅 분리  (0) 2022.11.21
[Node.js] View 분리  (0) 2022.11.20
[Node.js] 서버에 로그인 화면 적용시키기  (0) 2022.11.20
[Node.js] express 서버 띄우기  (0) 2022.11.18

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('여기는 로그인 화면입니다.');
});

  - 이후에 주소창에 'localhost:3000'을 치면

  - 'localhost:3000/login'을 치면

 

'back-end > Javascript' 카테고리의 다른 글

[Node.js] Controller 분리  (0) 2022.11.22
[Node.js] 라우팅 분리  (0) 2022.11.21
[Node.js] View 분리  (0) 2022.11.20
[Node.js] 서버에 로그인 화면 적용시키기  (0) 2022.11.20
[Node.js] https 서버 띄우기  (0) 2022.11.18

1. HTML의 기본틀

<!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>
// 글씨 크기가 점점 작아짐
// 02. <p>
<p>단락</p>
<p title="단락제목, 단락에 마우스 오버시 툴팁 표시">단락</p>
// 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 등 순서가 있는 문자)
// 06. 강조
<em></em>            // Italic체(음성도 바뀜)
<i></i>                   // Italic체
<strong></strong>  // Bold체(음성도 바뀜)
<b></b>                 // Bold체
<u></u>                 // Underline
// 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>
// 문서 작성자와 연결할 수 있는 링크 삽입
// 12. 첨자
<p>e<sup>2</sup></p>
<p>e<sub>2</sub></p>

결과: e2, e2

 

// 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;

 -Raster Image:  픽셀로 저장되어 확대하면 깨짐(.bmp, .png, .jpg, .gif)

 -Vector Image: 모양이 저장되어 확대해도 안깨짐(.svg)

 -출처:MDN Web Docs

  <svg version="1.1" baseProfile="full" width="300" height="200" xmlns="http://www.w3.org/2000/svg">
    <rect width="100%" height="100%" fill="black" />
    <circle cx="150" cy="100" r="90" fill="blue" />
  </svg>

 

// 21. 표
<table>
  <tr>
    <td>1행 1열</td>
    <td>1행 2열</td>
    <td>...</td>
    <td>1행 n열</td>
  </tr>
  <tr>
    <td>2행 1열</td>
    <td>2행 2열</td>
    <td>...</td>
    <td>2행 n열</td>
  </tr>
  <tr>
    ...
  </tr>
  <tr>
    <td>m행 1열</td>
    <td>m행 2열</td>
    <td>...</td>
    <td>m행 n열</td>
  </tr>
</table>
// <td> 대신 <th> 쓰면 표의 글씨가 굵어지고 표의 각 행렬의 제목(header)을 표시
// <td>&nbsp</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에 속해있는지 연결
1행 1열 1행 2열 1행 3열 1행 4열
cell1 cell2 cell3
2행 2열 2행 3열 2행 4열
3행 1열 3행 2열 3행 3열 3행 4열
4행 1열 4행 2열 4행 3열 4행 4열

'Front-end > HTML' 카테고리의 다른 글

[HTML] 로그인 화면 만들기  (0) 2022.11.18

1. 정규방정식

 -선형 회귀 모형에서 비용함수(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 배열 생성

X_b=np.array([[1.09521816,1],
                     [1.40637785,1],
                     [1.06414927,1]])
y=np.array([[6.00397116],
                   [8.4618674],
                   [9.23386642]])

 

 2) 엑셀에서 배열 생성

 

 3) X_b.T  :  전치

X_b배열을 복사하여 행/열 바꿔서 붙여넣기

 

 4) X_b.T.dot(X_b)  :  전치한 행렬과 원래 행렬 곱하기

     -"=MMULT(전치행렬, 원래행렬)" 식 작성 후 Ctrl+Shift+Enter

 

 5) np.linarg.inv(X_b.T.dot(X_b))  :  역행렬

     -"=MINVERSE(행렬)" 식 작성 후 Ctrl+Shift+Enter

 

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배 늘어남

 -정규방정식이나 다른 알고리즘으로 학습된 선형 회귀모델은 예측이 매우 빠름

  (예측 계산 복잡도는 샘플 수와 특성 수에 선형적으로 샘플(또는 특성)이 두배 증가하면 걸리는 시간도 거의 두배 증가)

 

 

1. 사용 데이터

 -사이킷런에서 제공하는 MNIST 데이터셋(손글씨 데이터셋)

# 사이킷런에서 기본적으로 제공하는 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라고 분류해냄

 

 -점수 확인하기

 -0~9까지 각 레이블에 대한 점수를 계산하여 가장 높은 점수를 가진 레이블로 예측함

 -레이블이 5일때 가장 높은 점수일 것으로 예상

some_digit_scores=svm_clf.decision_function([some_digit])
some_digit_scores

### 결과 ###
array([[ 1.72501977,  2.72809088,  7.2510018 ,  8.3076379 , -0.31087254,
         9.3132482 ,  1.70975103,  2.76765202,  6.23049537,  4.84771048]])

 -5일때 9.31점으로 가장 높은 점수가 나와 분류 결과가 5로 출력됨

 

 -분류한 모든 클래스 출력하기

svm_clf.classes_

### 결과 ###
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=uint8)

 

 

 -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개인 다중 레이블임

 -각 레이블은 0~255까지의 숫자를 가질 수 있는 다중 출력 분류가 가능한 데이터셋임

# MNIST 이미지의 픽셀 강도에 잡음 추가
noise = np.random.randint(0, 100, (len(X_train), 784))
X_train_mod = X_train + noise       # 독립변수는 잡음이 섞인 데이터
noise = np.random.randint(0, 100, (len(X_test), 784))
X_test_mod = X_test + noise
y_train_mod = X_train               # 예측해야하는 변수는 원래 데이터
y_test_mod = X_test

some_index = 5500
plt.subplot(121); plot_digit(X_test_mod[some_index])
plt.subplot(122); plot_digit(y_test_mod[some_index])
plt.show()

 

 -좌측은 노이즈를 섞은 데이터로 훈련기에 넣으면 노이즈를 제거하여 원래 이미지를 예측해낼 것

 -우측은 원래의 이미지

knn_clf.fit(X_train_mod,y_train_mod)
clean_digit=knn_clf.predict([X_test_mod[some_index]])
plot_digit(clean_digit)

 -노이즈가 있던 왼쪽의 이미지를 분류기에 넣어 분류기가 원래 이미지를 분류해낸 모습

+ Recent posts