ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [08.02] Day1 - '위대한 출발!'🎉
    네이버 부스트캠프 AI Tech 2기 2021. 8. 2. 22:59
    728x90
    반응형

    🤩 Basic Computer class for newbies(Python 1-1강)

    [수업 내용]

    강사님 : 최성철 교수님

    딥러닝을 배우기 위한 숨겨진 Class!

    컴퓨터를 사용하는데 좀 자신 없는 사람들을 위한 사람들을 위한 강의이다.

    Why? 우리는 의외로 정통 컴퓨터를 사용하는 것보다는 모바일 쪽에 더 친숙하고 그쪽에 이해가 더 높다..

    그런 사람들을 위해서 코딩, 딥러닝 등등 같은 것을 사용하기 위해서 컴퓨터를 좀 더 깊게 공부해본다!

    컴퓨터 OS

    우리의 프로그램이 동작할 수 있는 구동 환경

    HW 자체적으로는 컴퓨터가 될 수 없다, SW가 있어야만 비로소 컴퓨터가 된다. 이런 SW를 HW에 연결/연동 해 줄 수 있도록 도와주는 것이다. + 우리의 프로그램들은 OS에 의존적이다. 그래서 운영체제에 맞게 실행되는 프로그램들이 호환적이다.(Python은 독립적이다.)

    File System

    OS에서 파일을 저장하는 트리구조 저장체계

    윈도우라면 win + E : 파일 탐색기를 켜고 파일을 확인할 수 있다.

    • Directory : 폴더(파일과 디렉토리를 포함할 수 있다.)
    • File : 컴퓨터에서 정보를 저장하는 논리적 단위(파일명 + 확장자)

    윈도우 같은 것은 C드라이브를 기점으로 파일 시스템이 시작(맥은 Root(/)).

    경로 - 컴퓨터 파일의 고유 위치, 트리구조상 노드로 연결

    절대 경로 - 루트 디렉토리부터 파일위치까지 경로

    상대 경로 - 현재 있는 디렉토리부터 target 파일위치까지 경로 (.. → 상위 폴더)

    Terminal = Console = CMD창

    예전에는 Terminal환경(CLI 환경)에서 코딩을 하곤했다. 현재는 GUI 환경에서 많은 프로그램을 실행하고 코드도 짠다.

    : Text를 사용해서 컴퓨터에 명령을 입력하는 인터페이스 체계

    Windows - CMD window, Windows Terminal(win + R → CMD입력, 빠른실행 → Terminal입력)

    MAC, Linux - Terminal

    (Cmder 같은 부가적인 환경도 있다.)

    각 터미널에서는 프로그램을 작동하는 Shell이 존재(Core에 명령어가 들어가면 다시 return해준다. power shell, cmd창, bash shell 등등 Shell은 많다.) → Shell 마다 다른 명령어를 사용한다.

    [회고]

    copy 명령어 : $copy ../../abc.txt ./ → 앞의 디렉토리 앞에 디렉토리에 abc.txt 파일을 현재 디렉토리에 복사해라!

    cd, ls, 등등 : Shell 명령어 복기

    시작하기 전에 아주 짧게 컴퓨터의 전체적인 시스템에 대해서 배우게 되었다.

    터미널 같은 것은 배치 명령어같은 것들을 모아서 나중에 한번에 실행하기 위함이기 때문에 어느정도 알고 있어야할 내용이다.

    리눅스 커맨드에 대해서 필요한 부분은 나중에 찾아보며 사용하고 익혀야겠다.


    🅿 파이썬 개요(Python 1-2강)

    [수업 내용]

    강사님 : 최성철 교수님

    Python에 대한 배경, 역사 등등 기본적인 개요에 대해서 정리한다.

    Python은 '귀도 반 로섬'이 1991년 발표했다.

    영국에 있는 "monty python's flying circus"에서 유래되었다고 한다.

    Python(피톤)은 원래 그리스 신화속 괴물 뱀이다.

    특징

    1. 플랫폼 독립적
    2. 인터프리터 언어
    3. 객체지향
    4. 동적 타이핑 언어
    5. 처음에 C로 구현되어있다.

    플랫폼 독립적 인터프리터 언어다? : 플랫폼 → OS 즉, 운영체제에 상관없이 작동되는 언어이다.(인터프리터는 소스코드를 바로 실행할 수 있게 지원하는 프로그램 실행 방법)

    객체지향, 동적 타이핑 언어다? :

    객체지향 - 실행 순서가 아닌 단위 모듈(객체) 중심으로 프로그램을 작성한다. (객체는 Method, Attributes들을 가지고 있다.)

    동적 타이핑 언어 - 프로그램이 실행하는 시점에 프로그램이 사용해야할 데이터에 대한 타입을 결정한다.

    왜 파이썬을 배워야할까?

    1. 일단 쉽고 간단, 다양하다. (즉, 인간이 이해하기 쉬운 문법으로 되어있다.)
    2. 다양한 라이브러리를 지원한다. (데이터 분석, AI 등등)
    3. 이미 널리 쓰이는 언어이다.

    [회고]

    (전공수업에서 배운 내용이지만 다시 정리한다.)

    컴파일러 : 소스코드를 기계어로 먼저 번역한다. 해당 플랫폼(OS)에 최적화되어 실행속도가 빠르게 프로그램을 실행한다. 단, 한번에 많은 기억장소가 필요하다. (ex. C++, JAVA, C, C#)

    인터프리터 : 소스코드를 실행시점에 해석해서 컴퓨터가 처리할 수 있도록 한다. 메모리는 적게 필요하지만 실행속도가 느리다. (ex. Python, Scala)

    I Love Python!! 😍

    Life is Short. You need Python.


    🐤 파이썬 코딩 환경(Python 1-3강) For Mac

    [수업 내용]

    강사님 : 최성철 교수님

    Python 과 Jupyter NoteBook을 설치하는 방법에 대해서 알려주셨다.

    Python install

    1. miniconda 에 들어가면 Python을 설치하는 파일을 받을 수 있다. → Terminal로 가서 Download폴더 → $bash Miniconda3-latest-MacOSX-x86_64.sh(M1은 다른 파일로 받아야 할 것이다.)
    2. 그냥 Python홈페이지에가서 설치파일을 받아도 설치할 수 있다.

    Jupyter & Colab

    일반적으로 파이썬 기본 실행환경은 python shell을 쓰고 코드 편집도구IDE를 쓰는 것이다.

    Jupyter (Julia + Python + R)

    주피터 : IPython 커널을 기반으로 한 대화형 파이썬 Shell

    기본적으로 일반적 Terminal Shell + 웹 기반 데이터 분석 NoteBook 제공

    미디어, 텍스트, 코드, 수식 등을 하나의 문서로 표현 가능하다.

    실행방법 : Anaconda Prompt실행 → $conda install jupyter → 설치완료 → 실행시키는 환경은 내가 실행시킬 디렉토리로 이동 → $jupyter notebook

    (: 나는 보통 Anaconda Navigator를 이용한다. GUI 너무 편안)

    Colab

    구글이 제공하는 개발 클라우드 기반의 주피터 노트북

    구글 드라이브 + GPU + jupyter 등 합쳐져서 사용자가 손쉽게 접근할 수 있다.

    여러 모듈을 기본 제공하고 있다는 장점도 있다.

    [회고]

    Local 주피터 노트북 환경에서 실행을 시키면 내 컴퓨터 메모리에 올라간다는 것을 의미한다.

    Edit Mode 와 Command Mode 두가지로 나눠진다. 이런 모드에서 단축키를 몇개 알아두면 좀 더 간편할 것이다.


    ⭐️ 벡터가 뭐예요? (AI Math 1강)

    [수업 내용]

    강사님 : 임성빈 교수님

    벡터의 기본적인 개념, Norm에 대해서 소개해주셨다. 또한 벡터 사이의 각, 거리, 내적에 대해서 배운다.

    벡터는 딥러닝에서 굉장히 중요한 개념이고, 선형대수학에의 기본 단위이다. 또한 Numpy에도 많이 사용되기 때문에 반드시 확실하게 내 것으로 만들어야 한다. 연산 뿐 아니라 공간에 대한 이해를 하는 것이 중요하다.

    Norm, Dot Product 같은 개념도 기하학적 성질 + 실제 ML에서 어떻게 사용되는 지 생각하면서 공부한다.

    벡터 : 숫자를 원소로 가지는 리스트(List) 또는 배열(Array) 이다. 원점으로부터 상대적 위치를 표현한다.

    세로로 나열되어 있다면 열벡터

    가로로 나열되어 있다면 행벡터

    벡터를 표현할 때는 보통 np.array([]) 와 같은 형태로 표현한다. → 행벡터로 처리한다.

    벡터의 차원 = 벡터의 원소 갯수

    1. 벡터는 공간에서 한 점을 나타낸다. 2차원 이상으로 갈때는 x, y .. 축에 대한 값으로 벡터(점)을 표현할 수 있다. 인공지능에서는 훨씬 큰 차원을 다루게 된다.(N개의 축을 가지는 점으로 표현하기 때문)

    원점으로 부터 상대적 위치를 표현 → 벡터(화살표로 벡터를 표시)

    1. 벡터에 숫자를 곱하면 길이만 변한다.(스칼라 곱)[1 < a : 길이를 늘어나도록]
    2. [a < 0 : 방향이 반대로]
    3. [0 ≤ a < 1 길이를 줄이도록 ]
    4. 두 벡터의 차원이 같다면 덧셈과 뺄셈이 가능하다. (같은 모양이 아닌 경우는 연산이 불가능하다. → Error)벡터의 뺄셈은 방향을 뒤집은 덧셈이다.
    5. 벡터의 덧셈은 상대적 위치 이동을 의미(표현)한다.
    6. 두 벡터의 차원이 같으면 성분곱(Hadamard product, ElementWise Product) 이 가능하다. : 성분끼리 곱한다. (Numpy에서 * 연산)

    🔴 Norm

    : 원점에서부터의 거리를 말한다.

    주의! 임의의 차원 d에 대해 성립하는 것을 명심한다.

    • L1 - Norm(Robust 학습, Lasso 회귀)
      : 각 성분의 변화량의 절대값을 모두 더한다.Norm1 = np.abs(np.array)return Norm1
    • Norm1 = np.sum(Norm1)
    • 구하는 법
    • L2 -Norm(Laplace 근사, Ridge 회귀)Norm2 = np.array * np.arrayNorm2 = np.sqrt(Norm2)
    • return Norm2
    • Norm2 = np.sum(Norm2)
    • : 피타고라스 정리를 이용해 유클리드 거리를 계산한다.

    벡터 사이의 거리 계산 & 각도

    L1, L2 Norm을 이용해서 두 벡터사이의 거리, 각도를 계산한다.

    거리는 L1 Norm을 이용한다.

    두 벡터 사이의 거리를 계산할 때는 벡터의 뺄셈을 이용한다. (뺄셈은 거꾸로해도 거리는 똑같다)

    각도는 L2 Norm을 이용한다.(내적, L2)

    제2cos 법칙을 이용해서 0벡터, X벡터, Y벡터 3가지 벡터를 가지고 두 X, Y 벡터의 각을 구할 수 있다.

    d 차원에서도 두 벡터의 각도를 구하는 것도 가능할까? Yes. 마찬가지로 L2 Norm을 이용해서 구한다.

    🟡 내적

    : 내적은 정사영된 벡터의 길이와 관련되어 있다.

    X를 Y에 정사영 시킨 Proj(x)는 X의 L2-Norm에 cos(theta)를 곱한 것이다. 자, 내적은 이 정사영 시킨 Proj(x)를 벡터 Y의 길이만큼 조정한 값이다.

    구하는 방법 : [X벡터 L2 - Norm * cos(theta)] = Proj(x) * Y벡터 L2 - Norm

    X벡터를 Y에 정사영한 벡터를 Y의 길이만큼 조정시킨 벡터이다.

    → 두 벡터의 유사도를 측정하는데 사용한다.

    [회고]

    Norm에 따라서 기하학적 성질이 달라지기 때문에 다른 Norm을 알고 있어야 한다.

    Why? 거리를 구할때는 같은 1까지 거리더라도 기하학적 성질에 따라서 거리가 다르기 때문이다. 이런 다른 기하학적 성질을 이용해서 머신러닝에서 다양한 방식으로 최적화를 진행한다.

    기계학습에 따라 목적이다르다.

    L1 - Norm : (Robust 학습, Lasso 회귀)

    L2 - Norm : (Laplace 근사, Ridge 회귀)

    두 벡터의 각도를 계산하는 방법

    1. 두 벡터를 가지고 np.inner() 연산을 진행한다.(내적)
    2. 각각의 L2 - Norm을 계산하여 곱한다.
    3. 내적 / L2곱을 구한다.
    4. np.arccos(내적 / L2 곱) (Arc 코사인 함수를 이용하여 각도를 계산한다.)

    [궁금한 점]

    1. L1, L2 Norm에 대해서 확실하게 정리하고 간다.
    2. L2 Norm을 구할때, np.linalg.norm 함수는 어떻게 사용할까?

    🗓 행렬이 뭐예요? (AI Math 2강)

    [수업 내용]

    강사님 : 임성빈 교수님

    벡터는 숫자를 원소로 가지는 1차원 배열이었다.

    행렬은 벡터를 원소로 가지는 2차원 배열이다.

    ex ) [[1,2],[3,4]]

    행(row) → [1,2], [3,4]

    열(column)

    Numpy에서는 행벡터를 원소로 가진다는 것을 명심한다.

    일반적인 행렬 (N x M)

    N개의 행벡터로 이루어져 있고, M개의 열로 이루어져 있다.

    이때 하나의 행벡터는 M개의 원소로 되어있다.

    정리 : 이 행렬은 N개의 행벡터를 가지고 있으며, 각 행벡터는 M개의 원소를 가지고 있다.

    Index → (행번호, 열번호)

    행렬은 벡터들의 모임이라고 이해한다. 즉 여러 점들, 데이터들을 모은 것을 의미한다.

    Transpose matrix(전치행렬)

    행과 열의 인덱스를 바꾼 행렬이다. 우측 상단에 T로 표시한다.

    행렬 : X(N,M) → X^T(M,N) , 벡터 : X(1,N) → X^T(N,1)

    1. 행렬의 덧셈, 뺄셈, 성분곱, 스칼라곱

    덧셈과 뺄셈은 각 행렬의 구성성분의 연산으로 이해하기 쉽다.

    성분곱 또한 두 행렬의 원소들끼리의 곱으로 연산하면 된다.

    스칼라곱 모든 성분에 곱해주면 된다.

    2. 행렬의 곱셈

    두 행렬의 i번째 행벡터와 j 번째 열벡터 사이의 내적을 성분으로 가지는 행렬을 계산한다.

    조건 : 행렬곱은 X의 열개수와 Y의 행의 개수가 같아야 한다. (a,b) x (b,c)

    (Numpy : X @ Y로 곱한다.)

    3. 행렬의 내적

    np.inner()함수는 i 번째 행벡터와 j 번째 행벡터 사이의 내적을 성분으로 가지는 행렬을 계산한다.

    조건 : X의 행의 개수와 Y의 행의 개수가 같아야 한다.

    → 이는 X와 Y^T의 matrix-multiply의 결과와 동일하다.

    행렬의 이해

    🔴 하나의 벡터(N차원)를 다른 차원(M차원)으로 보낼(매핑) 수 있도록 하는 연산을 행렬로 표현할 수 있다.

    다시말해 행렬은 벡터공간에서 사용되는 연산자(Operator)로 이해한다.

    🟡 행렬 곱을 통해 패턴을 추출할 수 있고, 데이터 압축도 가능하다. → 모든 선형변환(linear transform)은 행렬곲으로 계산할 수 있다.

    역행렬(Inverse Matrix) 과 유사역행렬(Pseudo-Inverse)

    A 행렬 연산을 되돌릴 수 있도록 하는 연산자. (A^-1로 표시한다)

    구하는 법 : np.linalg.inv(X) → X의 역행렬

    조건 : 행과 열의 숫자가 같아야 한다. 또한 determinant가 0이 아니어야 한다.

    만약 행과 열이 다른다면? : Pseudo-inverse(유사 역행렬) 또는 Moore-Penrose(무어 펜로스 역행렬)을 이용한다. (A^+ 로 표시한다)

    → 주어진 행렬에서, 행의 개수가 많느냐? 열의 개수가 많느냐? 에 따라서 연산이 다르다는 것을 주의!

    구하는 법 : np.linalg.pinv(X) → X의 유사역행렬

    [회고]

    Numpy에서 내적을 구할때와 수학에서 말하는 내적의 개념이 다르니 주의한다!

    유사 역행렬을 연립방정식에서 응용할 수 있다는 것을 알았다.

    선형회귀 분석에서 (변수 < 식의 계수(베타)) 같은 경우, 곱셈의 방정식을 푸는 것이 불가능하지만, 이 또한 유사역행렬을 이용하게 된다면 해를 구할 수 있게 된다.

    우리가 예측한 yhat과 정답에 해당하는 y와의 차이(L2-Norm)가 최소가 될때 선형 회귀식을 잘 찾았다고 표현할 수 있다. 이때 무어 펜로스 역행렬을 구하면 최적의 Beta 계수들을 구할 수 있게 된다.

    sklearn.linear_model의 LinearRegression을 통해서 선형 회귀식을 구할 수있다.

    무어 펜로스 역행렬을 이용하여 찾은 선형 회귀식 ≠ sklearn에서 제공하는 선형 회귀식

    Why? : sklearn 같은 경우는 자동으로 intercept 항을 추가해 주지만, Moore-Penrose 역행렬은 Default로 제공하지는 않기 때문이다. 그래서 이 부분을 추가해 준다.


    🤔 경사하강법(순한맛) (AI Math 3강)

    [수업 내용]

    강사님 : 임성빈 교수님

    미분과 Gradient Vector에 대해서 배운다. 경사하강법의 알고리즘과 실제 코드에서 구현을 배운다.

    접선의 기울기를 이용해 함수의 최소값으로 점을 이동시키는 원리를 배운다.

    변수가 벡터라면, 편미분을 이용해서 구한 Gradient Vector를 통해 d-차원으로 경사하강법을 확장할 수 있다.

    경사하강법을 이해하기 전에 미분의 개념을 이해한다.

    🔴 미분

    변수의 움직임에 따른 함수값의 변화를 측정하기 위한 도구.(최적화에 많이 사용)

    미분의 개념을 확실하게 이해한다!

    미분이란 : 함수 f에서 (x, f(x)) 라는 점에서의 접선의 기울기를 의미한다. (이때 위의 식처럼 h를 0으로 보내게 된다.) → ⭐️ 현재 점에서 어느 방향으로 점을 움직여야 함수값이 증가하나/감소하나 알 수 있다.(중요!)

    함수를 증가시키고 싶다면 미분값을 더해준다.

    함수를 감소시키고 싶다면 미분값을 빼준다.

    이때!! 주목,

    미분값을 더하면 경사상승법 (함수의 극대값을 구하기 위해)

    미분값을 빼면 경사하강법 (함수의 극소값을 구하기위해)

    이라고 한다.

    🟡 변수가 벡터라면?

    : 편미분을 사용한다.(ei → i 번째 값만 1이고 나머지는 0인 단위 벡터)

    d 차원 벡터를 입력으로 가지는 함수를 편미분을 d 개만큼 계산할 수 있다. 이 변수 별로 편미분을 계산한 벡터를 Gradient Vector라고 한다. (nabla f라고 부른다.)

    이 nabla f 벡터를 이용하여 변수 x = (x1, ... xd) 를 동시에 업데이트할 수 있다.

    ex) f(x,y) = x^2 + 2y^2

    [회고]

    Python에서 미분을 계산해주는 함수!

    #미분을 도와주는 코드
    import sympy as sym
    from sympy.abc import x, y
    
    sym.diff(sym.poly(x로 표현된 다항식), x) -> x로 미분하라!

    함수를 증가하거나 감소할때, 미분값을 더해주거나 빼주는 관계는 함수의 양, 음의 상관없이 미분값을 더하면 함수가 증가하고, 미분값을 빼면 함수가 감소한다는 사실!

    경사하강법 알고리즘

    Input : gradient(미분을 계산하는 함수), init(시작점), lr(학습률), eps(알고리즘 종료조건)

    Output : var

    컴퓨터에서 미분값이 0이되는 경우는 정말 극소수이기 때문에 eps라는 알고리즘 종료조건을 걸어서 경사하강법을 종료시킨다.

    learning rate(하이퍼파라미터)를 곱하는 이유 : 미분을 통해 업데이트하는 속도를 조절하는 역할. 조심해서 다루는 것이 좋다.

    벡터인 경우는 eps에 조건을 설정할때 벡터의 Norm을 설정하면 되는 것만 차이이다.

    경사하강법 & SGD 에서는 Learning Rate 와 학습 횟수가 중요한 하이퍼 파라미터 역할이다.


    🌶 경사하강법(매운맛) (AI Math 4강)

    [수업 내용]

    강사님 : 임성빈 교수님

    경사하강법의 단점을 보완하는 확률적 경사하강법(stochastic gradient descent)을 배운다. 이전에 배운 무어-펜로즈 역행렬을 활용해 선형회귀분석과 비교해서, 선형 모델 이외에 적용 가능한 경사하강법-선형회귀분석 방법을 배운다. 경사하강법 알고리즘 수식을 정확히 알고 넘어간다!

    L2-Norm을 가장 최소화하는 선형 모델을 찾을 때 무어-펜로즈 역행렬을 이용해서 계수를 구할 수 있다.

    무어-펜로즈 역행렬을 활용한 선형회귀 분석 vs 경사하강법

    🔴 경사하강법으로 선형회귀계수 구하기

    선형 모델의 목적 : 두 벡터의 차이를 최소화하는 B를 찾는 것

    → 그럼 이것을 경사하강법으로 푼다? B로 미분한 다음 → 주어진 B에서 미분값을 빼주면 된다!!! 위에서 배운내용 😄

    경사하강법 과정

    학습률 람다를 베타에 대한 편미분인 그레디언트 벡틀를 곱해서 B(t)와 빼주면 된다. 아래는 정리한 공식

    경사하강법은 적절한 학습률과 학습횟수를 선택했을 때 수렴이 보장이 된다.

    단점 : 비선형회귀(볼록함수가 아닐때) 문제 경우는 수렴이 항상 보장하지 않는다.

    🟡 확률적 경사하강법(SGD)

    확률적 경사하강법은 모든 데이터를 통해 Gradient를 계산하는 것이 아니라, 일부나 한개를 활용해서 업데이트하게 된다. (한개 - sgd, 일부 - mini-batch sgd 라고 부른다.)

    장점 : 연산 자원을 전체 다쓰는 것이 아니기 때문에 조금 더 효율적이다. 학습 속도가 더 빠르다.

    원리 :

    경사하강법 - 전체데이터를 가지고 목적식의 그레디언트 벡터를 계산

    SGD - 미니배치를 가지고 그레디언트 벡터를 계산, 미니배치는 확률적으로 선택하므로 목적식 모양이 바뀌게 된다.

    (연두색 - 경사하강법, 파란색 - SGD)

    [회고]

    되게 중요한 포인트이다. Why? : 우리가 이전에 구한 L2-Norm 과는 다르게 1 ~ n 까지의 차의 제곱의 평균값을 구하고 이의 제곱근을 활용해서 Norm을 구한다.

    이를 Bk를 이용해서 편미분하면 결국 아래와 같은 식이 생긴다.

    → 이게 k번째 계수 Bk의 목적식의 편미분과 동일하게 된다.

    경사하강법 기반 선형회귀 알고리즘 수도코드

    for i in range(학습횟수):
        error = Y - X @ beta
        grad = - np.transpose(X) @ error
        beta = beta - lr * grad

    확률적 경사하강법을 사용하는 이유

    1. Non-Convex한 그래프에 경우에 최적화하는 것에서 그냥 경사하강법으로는 한계가 있다. 그렇기 때문에 확률적 경사하강법(SGD)를 이용한다. 물론 전체 데이터를 통해서 Gradient 를 구해야 하지만, 일부 데이터로 Gradient를 구하는 것도 거의 비슷한 성능을 내기 때문에 사용한다고 한다. (만능은 아님)
    2. Saddle Point라고 하는 지점이 있다. 이는 Non-Convex 함수에서 경사하강법을 쓰면 극소점을 탈출하지 못하고 갇혀버리는 경우가 발생하지만, SGD 같은 경우는 매번 Gradient Vector를 계산하기 때문에 극소점을 탈출하는 것이 가능하다 → 이것이 바로 Non-Convex에서 최적화가 가능한 이유이다.

    [궁금한 점]

    분명 SGD에도 문제가 있을 것이다. 이는 어떻게 해결할까? 다른 최적화 알고리즘은 뭐가 있을 까?

    또한 일부 데이터만 이용한다는 것은 매번 최적이 아닐 것이다. 이를 보완하는 알고리즘은 뭐가 있을까?

    반응형
Designed by Tistory.