페이지

2024년 10월 27일 일요일

JWT 토큰 생성과 유효성 확인 과정

JWT 토큰 생성과 유효성 확인 과정

API 서비스를 개발하고 이에 대한 접근 권한을 제어하기 위하여 JSON Web Token(JWT)을 활용할 수 있습니다. 이 문서에서는 JWT 토큰의 생성과 유효성 확인 과정을 그림과 Python 코드를 사용하여 설명합니다. 전자서명 알고리즘으로는 HS256을 사용하였습니다.

다음은 토큰 생성과 유효성 확인 과정에서 사용하게 될 Base64 인코딩/디코딩 함수입니다.

import base64
import hashlib
import hmac

def base64_encode(input_as_bytes):
    b = base64.urlsafe_b64encode(input_as_bytes).decode('utf-8')
    return b.rstrip('=')

def base64_decode(input_as_string):
    padding = 4 - len(input_as_string) % 4
    input_as_string = input_as_string + '=' * padding
    return base64.urlsafe_b64decode(input_as_string.encode('utf-8')).decode('utf-8')

1. 토큰 생성

1.1. 다이어그램

1.2. Python 구현

def create_jwt_token(header_obj_str, payload_obj_str, secret):
    header = base64_encode(header_obj_str.encode('utf-8'))
    payload = base64_encode(payload_obj_str.encode('utf-8'))
    header_plus_payload = f'{header}.{payload}'
    
    m = hmac.new(secret.encode('utf-8'), digestmod=hashlib.sha256)
    m.update(header_plus_payload.encode('utf-8'))
    d = m.digest()
    signature = base64_encode(d)

    jwt_token = f'{header_plus_payload}.{signature}'
    return jwt_token

header_obj_str = '{\
"typ":"JWT",\
"alg":"HS256"\
}'

payload_obj_str = '{\
"iss":"fun-with-jwts",\
"sub":"AzureDiamond",\
"jti":"f6c1097f-cc48-4949-a627-8b94fc5e37ba",\
"iat":1596185001,\
"exp":1596185061\
}'

secret = 'my-secret'

jwt_token = create_jwt_token(header_obj_str, payload_obj_str, secret)
print('** JWT token **')
print(jwt_token)

실행 결과:

** JWT token **
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJmdW4td2l0aC1qd3RzIiwic3ViIjoiQXp1cmVEaWFtb25kIiwianRpIjoiZjZjMTA5N2YtY2M0OC00OTQ5LWE2MjctOGI5NGZjNWUzN2JhIiwiaWF0IjoxNTk2MTg1MDAxLCJleHAiOjE1OTYxODUwNjF9.UXvXY97CNcHv7LobrBagePBPeGiW2F-Z-nuINSmUy5k

입력

  • header object

    {
        "typ": "JWT",
        "alg": "HS256"
    }
    
  • payload object

    {
        "iss": "fun-with-jwts",
        "sub": "AzureDiamond",
        "jti": "f6c1097f-cc48-4949-a627-8b94fc5e37ba",
        "iat": 1596185001,
        "exp": 1596185061
    }
    
  • secret

    my-secret
    

출력

  • token

    eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJmdW4td2l0aC1qd3RzIiwic3ViIjoiQXp1cmVEaWFtb25kIiwianRpIjoiZjZjMTA5N2YtY2M0OC00OTQ5LWE2MjctOGI5NGZjNWUzN2JhIiwiaWF0IjoxNTk2MTg1MDAxLCJleHAiOjE1OTYxODUwNjF9.UXvXY97CNcHv7LobrBagePBPeGiW2F-Z-nuINSmUy5k
    

2. 토큰 유효성 확인

2.1. 다이어그램

2.2. Python 구현

def validate_jwt_token(token, secret):
    pos = token.rfind('.')
    header_plus_payload = token[:pos]
    signature = token[pos+1:]
    
    m = hmac.new(secret.encode('utf-8'), digestmod=hashlib.sha256)
    m.update(header_plus_payload.encode('utf-8'))
    d = m.digest()
    
    signature_derived = base64_encode(d)
    
    return signature_derived == signature

token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJmdW4td2l0aC1qd3RzIiwic3ViIjoiQXp1cmVEaWFtb25kIiwianRpIjoiZjZjMTA5N2YtY2M0OC00OTQ5LWE2MjctOGI5NGZjNWUzN2JhIiwiaWF0IjoxNTk2MTg1MDAxLCJleHAiOjE1OTYxODUwNjF9.UXvXY97CNcHv7LobrBagePBPeGiW2F-Z-nuINSmUy5k'
secret = 'my-secret'

is_valid = validate_jwt_token(token, secret)
print(f'** is_valid: {is_valid} **')

실행 결과:

** is_valid: True **

입력

  • token

    eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJmdW4td2l0aC1qd3RzIiwic3ViIjoiQXp1cmVEaWFtb25kIiwianRpIjoiZjZjMTA5N2YtY2M0OC00OTQ5LWE2MjctOGI5NGZjNWUzN2JhIiwiaWF0IjoxNTk2MTg1MDAxLCJleHAiOjE1OTYxODUwNjF9.UXvXY97CNcHv7LobrBagePBPeGiW2F-Z-nuINSmUy5k
    
  • secret

    my-secret
    

출력

  • is_valid

    True
    

Written with StackEdit.

2024년 10월 22일 화요일

Python 객체 저장 - pickle, pickletools

Python 객체 저장 - pickle, pickletools

pickle, pickletools 모듈 문서

pickle - Python object serialization

  • Protocol version 0 is the original “human-readable” protocol and is backwards compatible with earlier versions of Python.
  • Protocol version 1 is an old binary format which is also compatible with earlier versions of Python.
  • Protocol version 2 was introduced in Python 2.3. It provides much more efficient pickling of new-style classes. Refer to PEP 307 for information about improvements brought by protocol 2.
  • Protocol version 3 was added in Python 3.0. It has explicit support for bytes objects and cannot be unpickled by Python 2.x. This was the default protocol in Python 3.0–3.7.
  • Protocol version 4 was added in Python 3.4. It adds support for very large objects, pickling more kinds of objects, and some data format optimizations. It is the default protocol starting with Python 3.8. Refer to PEP 3154 for information about improvements brought by protocol 4.
  • Protocol version 5 was added in Python 3.8. It adds support for out-of-band data and speedup for in-band data. Refer to PEP 574 for information about improvements brought by protocol 5.

pickletools — Tools for pickle developers

명령 실행 예시

아래 소스 코드를 실행하여 튜플 (1, 2)를 x.pickle 파일로 저장합니다.

from pickle import dump, load

t = (1, 2) 

with open('x.pickle', 'wb') as f: 
    dump(t, f) 

print(f'Write Done: {t}')

x.pickle 파일에 대한 pickle, pickletools 명령 실행 예시입니다.

$ python -m pickle x.pickle 
(1, 2) 
$ python -m pickletools x.pickle 
    0: \x80 PROTO 3 
    2: K BININT1 1 
    4: K BININT1 2 
    6: \x86 TUPLE2 
    7: q BINPUT 0 
    9: . STOP 
highest protocol among opcodes = 2

위에서 pickle은 x.pickle 파일에 저장되어 있는 객체를 복원하여 출력하고 pickletools는 x.pickle 파일을 파싱하여 출력합니다.

Keras 2와 3에서 Tokenizer 객체 저장 및 읽기 테스트

Tokenizer 객체의 pickle 파일은 Keras 2와 Keras 3 간에 호환되지 않습니다. 테스트를 통해서 그 이유를 파악해 봅니다.

아래 소스 코드는 Tokenizer 객체를 tokenizer.pkl 파일에 저장합니다.

from tensorflow.keras.preprocessing.text import Tokenizer 
from pickle import dump 

sample_texts = [ 
    '산악용 자전거' , 
    '자전거 전조등 후미등' , 
    '온라인 상품권' , 
    '온라인 상품 세트' 
] 

sample_tokenizer = Tokenizer() 
sample_tokenizer.fit_on_texts(sample_texts) 

dump(sample_tokenizer, open('tokenizer.pkl', 'wb'))

위 코드를 Keras 2에서 실행하여 tokenizer.pkl 파일을 생성하고 Keras 3에서 읽기를 시도합니다.

$ python -m pickle tokenizer.pkl 
... 
ModuleNotFoundError: No module named 'keras.src.preprocessing'

Keras 2에서 생성한 tokenizer.pkl 파일을 Keras 3에서 읽는 과정에서 오류가 발생했고 pickletools로 tokenizer.pkl 파일에 대하여 좀 더 자세한 정보를 출력해 봅니다.

$ python -m pickletools tokenizer.pkl 
     0: \x80 PROTO 4 
     2: \x95 FRAME 587 
    11: \x8c SHORT_BINUNICODE 'keras.src.preprocessing.text' 
    41: \x94 MEMOIZE (as 0) 
    42: \x8c SHORT_BINUNICODE 'Tokenizer' 
    53: \x94 MEMOIZE (as 1) 
    ...

이번에는 위 코드를 Keras 3에서 실행하여 tokenizer.pkl 파일을 생성하고 Keras 2에서 읽기를 시도합니다.

$ python -m pickle tokenizer.pkl 
... 
ModuleNotFoundError: No module named 'keras.src.legacy'

Keras 3에서 생성한 tokenizer.pkl 파일을 Keras 2에서 읽는 과정에서 오류가 발생했고 pickletools로 tokenizer.pkl 파일에 대하여 좀 더 자세한 정보를 출력해 봅니다.

python -m pickletools tokenizer.pkl 
     0: \x80 PROTO 4 
     2: \x95 FRAME 594 
    11: \x8c SHORT_BINUNICODE 'keras.src.legacy.preprocessing.text' 
    48: \x94 MEMOIZE (as 0) 
    49: \x8c SHORT_BINUNICODE 'Tokenizer' 
    60: \x94 MEMOIZE (as 1) 
    ...

Keras 2와 비교하여 Keras 3에서 Tokenizer를 저장하고 읽을 때 사용하는 모듈의 이름이 바뀌었고 이름 중에 legacy가 들어가 있습니다.

  • Keras 2: keras.src.preprocessing.text
  • Keras 3: keras.src.legacy.preprocessing.text

이것은 아마도 아래와 같이 Tokenizer 상태를 DEPRECATED로 바꾼 것과 관련이 있어 보입니다.

  • Tokenizer는 DEPRECATED 상태

※ 참고: preprocessing 모듈 전체가 DEPRECATED 상태

정리

  • Tokenizer 객체의 pickle 파일은 Keras 2와 Keras 3 간에 호환되지 않습니다. 호환되지 않는 이유는 Tokenizer 직렬화에 사용하는 Keras 모듈의 이름이 바뀌었기 때문이며 pickle 프로토콜 버전과는 관련이 없습니다.

Written with StackEdit.

2024년 10월 21일 월요일

TensorFlow 버전과 Keras 2 또는 3 버전

TensorFlow 버전과 Keras 2 또는 3 버전

개요

실습 목적

  • Python 버전에 따라 설치할 수 있는 TensorFlow 버전이 어떻게 달라지는지 파악합니다.
  • TensorFlow를 설치하면 Keras도 설치됩니다. 어느 버전의 TensorFlow부터 Keras 3가 설치되는지 파악합니다.
  • Keras 2.15.0에서 발생하는 TextVectorization 객체 직렬화 문제가 Keras 3에서는 발생하지 않는지 파악합니다.

실습 환경

Windows 10 환경에서 실습하고 문서를 작성하였습니다. 가상 환경 도구로는 Python 기본 라이브러리에 포함되어 있는 venv를 사용하였습니다.

SW 버전
Python
  • 3.9.3
  • 3.12.4
  • pip
  • 22.0.4
  • 24.0
  • TensorFlow
  • 2.15.1
  • 2.16.2
  • Keras
  • 2.15.0
  • 3.4.1
  • Python 3.9.13 + TensorFlow

    Python 설치 파일 다운로드 위치

    설치된 Python과 pip 버전 확인

    >python --version
    Python 3.9.13
    >pip --version
    pip 22.0.4 from C:\Users\user1\AppData\Local\Programs\Python\Python39\lib\site-packages\pip (python 3.9)
    

    설치 가능한 TensorFlow 버전 출력

    >pip index versions tensorflow
    WARNING: pip index is currently an experimental command. It may be removed/changed in a future release without prior warning.
    tensorflow (2.17.0)
    Available versions: 2.17.0, 2.16.2, 2.16.1, 2.15.1, 2.15.0, 2.14.1, 2.14.0, 2.13.1, 2.13.0, 2.12.1, 2.12.0, 2.11.1, 2.11.0, 2.10.1, 2.10.0, 2.9.3, 2.9.2, 2.9.1, 2.9.0, 2.8.4, 2.8.3, 2.8.2, 2.8.1, 2.8.0, 2.7.4, 2.7.3, 2.7.2, 2.7.1, 2.7.0, 2.6.5, 2.6.4, 2.6.3, 2.6.2, 2.6.1, 2.6.0, 2.5.3, 2.5.2, 2.5.1, 2.5.0
    

    TensorFlow 2.15.1 설치

    1. 가상환경 만들기

      >python -m venv py39_tf215
      >py39_tf215\Scripts\activate.bat
      
    2. TensorFlow 설치

      (py39_tf215) >pip install tensorflow==2.15.1
      
    3. TensorFlow와 Keras 버전 출력

      (py39_tf215) C:\DevEnv>python -c "import tensorflow; print(tensorflow.__version__)"
      2.15.1
      (py39_tf215) C:\DevEnv>python -c "import keras; print(keras.__version__)"
      2.15.0
      
    4. 가상환경 종료

      (py39_tf215) C:\DevEnv>py39_tf215\Scripts\deactivate.bat
      

    TensorFlow 2.16.2 설치

    1. 가상환경 만들기

      >python -m venv py39_tf216
      >py39_tf216\Scripts\activate.bat
      
    2. TensorFlow 설치

      (py39_tf216) >pip install tensorflow==2.16.2
      
    3. TensorFlow와 Keras 버전 출력

      (py39_tf216) C:\DevEnv>python -c "import tensorflow; print(tensorflow.__version__)"
      2.16.2
      (py39_tf216) C:\DevEnv>python -c "import keras; print(keras.__version__)"
      3.4.1
      
    4. 가상환경 종료

      (py39_tf216) C:\DevEnv>py39_tf216\Scripts\deactivate.bat
      

    Python 3.12.4 + TensorFlow

    Python 설치 파일 다운로드 위치

    설치된 Python과 pip 버전 확인

    >python --version
    Python 3.12.4
    >pip --version
    pip 24.0 from C:\Users\user1\AppData\Local\Programs\Python\Python312\Lib\site-packages\pip (python 3.12)
    

    설치 가능한 TensorFlow 버전 출력

    >pip index versions tensorflow
    WARNING: pip index is currently an experimental command. It may be removed/changed in a future release without prior warning.
    tensorflow (2.17.0)
    Available versions: 2.17.0, 2.16.2, 2.16.1
    

    TensorFlow 2.16.1 부터 설치 가능하고 이 버전을 설치하면 Keras 3가 함께 설치됩니다.

    그렇다고 Keras 2를 설치할 수 없다는 것은 아닙니다. 아래와 같이 Keras 2, Keras 3에 해당하는 다양한 버전을 설치할 수 있습니다.

    >pip index versions keras
    WARNING: pip index is currently an experimental command. It may be removed/changed in a future release without prior warning.
    keras (3.4.1)
    Available versions: 3.4.1, 3.4.0, 3.3.3, 3.3.2, 3.3.1, 3.3.0, 3.2.1, 3.2.0, 3.1.1, 3.1.0, 3.0.5, 3.0.4, 3.0.3, 3.0.2, 3.0.1, 3.0.0, 2.15.0, 2.14.0, 2.13.1, 2.12.0, 2.11.0, 2.10.0, 2.9.0, 2.8.0, 2.7.0, 2.6.0, 2.4.3, 2.4.2, 2.4.1, 2.4.0, 2.3.1, 2.3.0, 2.2.5, 2.2.4, 2.2.3, 2.2.2, 2.2.1, 2.2.0, 2.1.6, 2.1.5, 2.1.4, 2.1.3, 2.1.2, 2.1.1, 2.1.0, 2.0.9, 2.0.8, 2.0.7, 2.0.6, 2.0.5, 2.0.4, 2.0.3, 2.0.2, 2.0.1, 2.0.0, 1.2.2, 1.2.1, 1.2.0, 1.1.2, 1.1.1, 1.1.0, 1.0.8, 1.0.7, 1.0.6, 1.0.5, 1.0.4, 1.0.3, 1.0.2, 1.0.1, 1.0.0, 0.3.3, 0.3.2, 0.3.1, 0.3.0, 0.2.0
    

    TextVectorization 객체 직렬화 문제

    Keras 2.15.0에서 오류 발생

    Keras 2.15.0에서 TextVectorization 객체 직렬화 문제가 발생합니다.

    Traceback (most recent call last):
      File "C:\DevWork\trvoid\GitHub\ml-study\python-basics\TextVectorization-basics.py", line 110, in <module>
        main(args)
      File "C:\DevWork\trvoid\GitHub\ml-study\python-basics\TextVectorization-basics.py", line 93, in main
        save_text_vectorization(text_vectorization, output_filepath)
      File "C:\DevWork\trvoid\GitHub\ml-study\python-basics\TextVectorization-basics.py", line 32, in save_text_vectorization
        pickle.dump(text_vectorization, f)
      File "C:\DevEnv\tensorflow215\lib\site-packages\tensorflow\python\framework\ops.py", line 314, in __reduce__
        return convert_to_tensor, (self._numpy(),)
      File "C:\DevEnv\tensorflow215\lib\site-packages\tensorflow\python\framework\ops.py", line 362, in _numpy
        raise core._status_to_exception(e) from None  # pylint: disable=protected-access
    tensorflow.python.framework.errors_impl.InvalidArgumentError: Cannot convert a Tensor of dtype resource to a NumPy array.
    

    해결 방법

    Keras 2.15.0에서의 TextVectorization 객체 직렬화 문제는 아래 문서에서 해결 방법을 찾을 수 있습니다.

    Keras 3.4.1에서는 정상 처리됨

    Keras 3.4.1에서는 TextVectorization 객체 직렬화가 정상적으로 처리됩니다.

    정리

    TensorFlow와 함께 설치되는 Keras 버전

    • TensorFlow 2.15.1 => Keras 2.15.0
    • TensorFlow 2.16.1 => Keras 3.4.1

    참고 사항

    • Python 3.12.4를 설치하면 TensorFlow 2.16.1부터 설치 가능
    • TensorFlow 2.16.1에서 Keras 2.15.0을 설치하는 것도 가능

    Written with StackEdit.

    2024년 9월 30일 월요일

    Circom으로 영지식 증명 시작하기

    Circom으로 영지식 증명 시작하기

    Circom 2 Getting started 문서를 따라하면서 작성한 문서입니다.

    • 실습 환경: Windows 11

    1. 소프트웨어 설치

    1. Downloads 페이지에서 circom Windows binary를 클릭하고 파일 circom-windows-amd64.exe 를 원하는 디렉토리에 저장합니다. 여기서는 파일 이름을 circom.exe로 바꾸고 아래 경로에 저장합니다.
      C:\DevTools\circom.exe
      
    2. snarkjs를 설치합니다.
      npm install -g snarkjs
      

    2. circuit 생성하기

    1. 아래 내용을 multiplier2.circom 에 저장합니다.

      pragma circom 2.0.0;
      
      /*This circuit template checks that c is the multiplication of a and b.*/  
      
      template Multiplier2 () {
         // Declaration of signals.
         signal input a;
         signal input b;
         signal output c;
      
         // Constraints.
         c <== a * b;
      }
      
      component main = Multiplier2();
      

    3. circuit 컴파일하기

    1. multiplier2.circom 파일을 컴파일합니다.

      circom multiplier2.circom --r1cs --wasm --sym --c
      

      컴파일 결과 다음과 같은 파일들이 생성됩니다.

      multiplier2.r1cs
      multiplier2.sym
      multiplier2_cpp\
          calcwit.cpp
          calcwit.hpp
          circom.hpp
          fr.asm
          fr.cpp
          fr.hpp
          main.cpp
          Makefile
          multiplier2.cpp
          multiplier2.dat
      multiplier2_js\
          generate_witness.js
          multiplier2.wasm
          witness_calculator.js
      

    4. witness 계산하기

    입력 값, 중간 신호, 출력 값을 witness라고 합니다.

    1. multiplier2_js 디렉토리로 이동합니다.

      cd multiplier2_js
      
    2. 아래 내용을 input.json 파일에 저장합니다.

      {"a": "3", "b": "11"}
      
    3. witness를 계산하여 witness.wtns 파일에 저장합니다.

      node generate_witness.js multiplier2.wasm input.json witness.wtns
      

    5. circuit을 영지식으로 증명하기

    증명을 생성하기 위하여 아래 두 파일을 사용합니다.

    • multiplier2.r1cs: 회로를 기술하는 제약사항 포함
    • witness.wtns: 모든 계산된 신호 값 포함

    여기서는 Groth16 zk-SNARK 프로토콜을 사용하고자 합니다. 이 프로토콜은 trusted setup을 필요로 하고 이 과정은 두 단계로 구성됩니다.

    • Powers of tau - circuit에 독립적
    • Phase 2 - circuit에 의존적
    5.1. Powers of Tau
    1. “powers of tau” ceremony 시작

      snarkjs powersoftau new bn128 12 pot12_0000.ptau -v
      
    2. ceremony에 기여

      snarkjs powersoftau contribute pot12_0000.ptau pot12_0001.ptau --name="First contribution" -v
      
    5.2. Phase 2
    1. Phase 생성

      snarkjs powersoftau prepare phase2 pot12_0001.ptau pot12_final.ptau -v
      
    2. .r1cs 파일과 연관된 .zkey 파일 생성

      snarkjs groth16 setup ..\multiplier2.r1cs pot12_final.ptau multiplier2_0000.zkey
      
    3. ceremony의 phase 2에 기여

      snarkjs zkey contribute multiplier2_0000.zkey multiplier2_0001.zkey --name="1st Contributor Name" -v
      
    4. verification key 내보내기

      snarkjs zkey export verificationkey multiplier2_0001.zkey verification_key.json
      
    5.3. 증명 생성
    1. circuit 및 witness와 연관된 증명을 생성합니다.

      snarkjs groth16 prove multiplier2_0001.zkey witness.wtns proof.json public.json
      

      위 명령은 다음 두 파일을 생성합니다.

      • proof.json: 증명 포함
        {
         "pi_a": [
          "13983182679953556458842508347137149918023927201985471435159758927740558853016",
          "17126988695844741179284716898691257368385629126542168835554345497343624722940",
          "1"
         ],
         "pi_b": [
          [
           "19140696255248677436910729284929621534969442224058440363361064354121562854264",
           "14257009146984576436317952378148206632799369157719061977972167958840456095938"
          ],
          [
           "7733560110556055181607998374149392520454003067800483949057392794063019344423",
           "2891268127818819654691304244486325795969498402824384674233464052246905803426"
          ],
          [
           "1",
           "0"
          ]
         ],
         "pi_c": [
          "21608704327549608970596366226125688092932784974035385756433099903167703539664",
          "19614360261086173764968380312474156102058426242974580364381559870892304372835",
          "1"
         ],
         "protocol": "groth16",
         "curve": "bn128"
        }
        
      • public.json: 공개 입력 및 출력 값 포함
        [
        	"33"
        ]
        
    5.4. 증명 검증
    1. 아래 명령을 수행하여 증명을 검증합니다.

      snarkjs groth16 verify verification_key.json public.json proof.json
      

      위 명령의 수행 결과는 다음과 같습니다.

      [INFO]  snarkJS: OK!
      

    6. 정리

    실습을 정상적으로 마쳤을 때 multiplier2_js 디렉토리의 파일 목록은 다음과 같습니다.

    generate_witness.js
    input.json
    multiplier2.wasm
    multiplier2_0000.zkey
    multiplier2_0001.zkey
    pot12_0000.ptau
    pot12_0001.ptau
    pot12_final.ptau
    proof.json
    public.json
    verification_key.json
    witness.wtns
    witness_calculator.js
    

    Written with StackEdit.

    2024년 7월 27일 토요일

    카이제곱 검정 제대로 이해하기

    카이제곱 검정 제대로 이해하기

    1. 개념 이해

    비교란 무엇인가?

    비교의 대상은 두 개이고 두 비교 대상의 차이를 살펴 보는 것이다.

    • 여기서는 관찰값기댓값의 차이를 사용한다.
      • 관찰값: 관찰을 통해 얻는 값
      • 기댓값: 관찰값에 대한 기댓값으로서 어떤 가정을 바탕으로 함.
    • 두 비교 대상에 대하여 비교할 수 있는 지점은 한 개일 수도 있고 여러 개일 수도 있다.

    비교의 목적은 무엇인가?

    • 관찰값관찰값을 비교 대상으로 삼을 수는 없는가?
      • 비교할 수 있다. 그런데 어떻게 비교할 것인가?
        • 기댓값을 찾고 관찰값들이 기댓값을 중심으로 어떤 분포를 따르는지 이해하는 것이 필요하다.
          • 왜 그런가?
            • 분포를 가정하지 않으면 흔히 일어나는 차이인지 아니면 드물게 일어나는 차이인지 객관적으로 표현하기가 어렵다.
    • 기댓값을 찾으려면 왜 그런 기대를 하게 되었는지와 관련하여 어떤 가정을 세워야 한다.
      • 그 가정 위에서 기댓값을 구하고 차이를 계산했더니 흔히 일어나는 차이에 해당하면 그 가정은 유효하다고 간주하고 드물게 일어나는 차이에 해당하면 그 가정은 유효하지 않다고 간주한다.
      • 결국 관찰값과 기댓값의 차이를 통해 가정이 유효한지 아닌지 판단하는 것이므로 비교의 목적은 어떤 가정을 채택할 것인지 버릴 것인지를 판단하기 위함이라고 말할 수 있다.

    2. 차이의 정도와 의미

    차이의 정도를 수치로 나타낼 수 있는가?

    비교 대상 간의 총체적인 차이는 무엇으로 정의할 것인가?

    • 개별 데이터 지점에서의 차이 값의 제곱의 합이라고 정의하자. 절대값의 합이 아니라 제곱의 합으로 정의한 이유는 수학적으로 다루기가 훨씬 쉽기 때문일 것이다.
    • 아래의 수식은 피어슨 카이제곱 통계량이고 이것으로 총체적인 차이를 계산한다. OiO_iii번째 데이터 지점에서의 관찰값, EiE_iii번째 데이터 지점에서의 기댓값을 나타낸다.

    χ2=i=1m(OiEi)2Ei(1) \chi^2 = \sum_{i=1}^{m} \frac {(O_i-E_i)^2}{E_i} \tag{1}

    특정 값의 총체적인 차이가 발생할 가능성은?

    • 차이의 정도에 대한 가정
      • 개별 데이터 지점에서 관찰값기댓값의 차이의 정도는 확률변수이고 정규분포를 따른다.
        • 오차에 대한 가정을 주로 이렇게 한다는 점을 참고하자.
        • 편차 제곱을 기댓값으로 나눔으로써 표준화를 시도한다.
      • 모든 데이터 지점에서 기댓값은 5 이상이어야 한다.
        • 대표본 가정
    • 그렇다면 총체적인 차이가 특정 값 이상일 가능성은?
      • 총체적인 차이 값은 확률변수이고 자유도를 파라미터로 하는 카이제곱 분포를 따른다.
        • 좀 더 정확하게 표현하자면 이런 경우의 분포를 카이제곱 분포라고 정의한 것이다.
      • 카이제곱 분포표에서 자유도와 유의수준에 해당하는 카이제곱 값을 찾을 수 있다.
        • 총체적인 차이가 카이제곱 분포표에서 찾은 카이제곱 값보다 작으면 흔히 일어날 수 있는 차이로 간주하고 그렇지 않으면 쉽게 일어나기 어려운 차이로 간주한다.

    차이의 정도가 유의미한가?

    1. 유의수준 α\alpha를 설정한다. 여기서는 α=0.05\alpha = 0.05로 하자.
    2. 자유도가 얼마인지 파악한다.
    3. 개별 데이터 지점에 대하여 기댓값을 파악하거나 계산한다.
    4. 피어슨 카이제곱 통계량을 계산한다.
    5. 카이제곱 분포표에서 자유도유의수준에 해당하는 카이제곱 값을 찾는다.
    6. 계산한 카이제곱 값이 분포표에서 찾은 카이제곱 값보다
      • 작으면 차이가 유의미하지 않다고 보고 대립가설을 기각한다.
      • 크면 차이가 유의미하다고 간주하고 대립가설을 채택한다.

    차이가 유의미하다는 것은 무슨 뜻인가?

    검정 목적별로 해석을 달리한다.

    • 적합도 검정 (Goodness-of-fit Test)
      • 주머니속 사탕 색깔의 구성 비율이 특정 비율을 따르는지? ⇒ 구성 비율에 대한 추측이 사실이 아닐 가능성이 높다.
    • 동질성 검정 (Test of Homogeneity)
      • 성별에 따른 메뉴 선호도가 유사한지? ⇒ 성별에 따른 메뉴 선호도가 유사하지 않을 가능성이 높다.
    • 독립성 검정 (Test of Independence)
      • 성별과 메뉴 선호도가 서로 관련이 없는지? ⇒ 성별과 메뉴 선호도가 서로 관련이 있을 가능성이 높다.

    다음 두 가지 설명이 동질성 검정과 독립성 검정의 차이를 이해하는데 도움이 될 것이다.

    • 독립성 검정의 결과
      • 서로 관련이 없다.
        • 동질성 검정 필요 없음
      • 서로 관련이 있다.
        • 동질성 검정의 결과
          • 유사하다.
          • 유사하지 않다.
            • 서로 관련은 있으나 유사하지는 않은 경우
              • 예를 들자면 남자가 좋아하는 메뉴는 여자가 싫어하고 남자가 싫어하는 메뉴는 여자가 좋아함
    • 동질성 검정의 결과
      • 유사하다.
        • 독립성 검정 필요 없음
      • 유사하지 않다.
        • 독립성 검정의 결과
          • 서로 관련이 없다.
          • 서로 관련이 있다.
            • 유사하지는 않으나 서로 관련은 있는 경우
              • 예를 들자면 남자가 좋아하는 메뉴는 여자가 싫어하고 남자가 싫어하는 메뉴는 여자가 좋아함

    카이제곱 검정 예를 좀 더 구체적으로 살펴 본다면?

    • 적합도 검정
      • 관찰값의 분포를 통해 모집단의 분포를 확인하는 검정
        • 예시: 주머니 속 사탕을 복원추출하여 파악한 색깔 구성 비율을 토대로 주머니 속 사탕 색깔이 골고루 섞여 있다고 볼 수 있는지?
          • 첫 번째 행은 관찰도수, 두 번째 행은 기대도수
          • 관찰도수와 기대도수의 차이를 사용하여 χ2\chi^2 구하기
    • 동질성 검정
      • 각기 다른 모집단으로부터 표본을 추출하여 특성 값에 따라 분류하고 두 모집단의 특성 값 비율이 유사한지 검정
        • 예시: 성별에 따른 메뉴 선호도가 유사한지?
          • 행은 집단, 열은 특성 값
          • 행의 주변도수 크기 고정
          • 열의 주변도수(marginal frequency)를 구하고 이들의 분포를 활용하여 각 행의 특성 값들에 대하여 기대도수 구하기
          • 관찰도수와 기대도수의 차이를 사용하여 χ2\chi^2 구하기
    • 독립성 검정
      • 두 종류 이상의 범주형 변수를 사용하여 자료를 분류하였을 때 변수들이 서로 독립적인지 검정
        • 예시: 성별과 메뉴 선호도가 서로 관련이 없는지?
          • 표본 크기 고정
          • 두 변수가 서로 독립적이다라는 전제로 기댓값 구하기
          • 관찰도수와 기대도수의 차이를 사용하여 χ2\chi^2 구하기

    카이제곱 검정의 세 종류는 서로 다른 목적을 가지고 있지만 공통점은 다음과 같다.

    • 관찰값기댓값의 차이를 계산한다. 관찰값과 관찰값의 차이를 계산하는 것이 아님에 주목하자.

    더 많은 예시

    • 적합도 검정
      • 멘델의 유전법칙에 의하면 4종류의 식물이 9:3:3:1의 비율로 나오게 되어 있다고 한다. 240그루의 식물을 관찰하였더니 120:40:55:25로 나타났다. 유의수준 5%로 적합도 검정을 하시오
      • 어느 공정의 부적합품률은 15%로 알려져있다. 시료를 80개 추출하여 검사한 결과 불량이 16개이다. 유의수준 5%로 적합도 검정을 하시오.
      • 두 정당에 대한 지지율이 한 달 전에는 54:46이었다.
    • 동질성 검정
      • 남,녀 각각 500명을 임의로 추출하였고, 성별에 따른 선호도가 관련성이 있는지, 유의수준 0.05에서 검정하시오.
      • 기존약과 신약의 효과 비교
      • 성별에 따른 흡연 여부의 분포
    • 독립성 검정
      • 영화 장르와 간식류 구매는 서로 연관이 있는지 검정하시오.
      • 보호구 착용과 부상 정도
      • 성별과 흡연 여부의 관련성

    3. 카이제곱 분포와 검정

    카이제곱 분포

    양의 정수 kk에 대하여 kk 개의 독립적이고 표준정규분포를 따르는 확률변수 X1X_1, …, XkX_k를 정의하면 자유도 kk의 카이제곱 분포는 확률변수

    Q=i=1kXi2(2) Q = \sum_{i=1}^{k} X_i^2 \tag{2}

    가 따르는 분포입니다.


    이미지 출처: 카이제곱 분포와 검정, 공돌이의 수학정리노트

    피어슨 카이제곱 통계량

    χ2=i=1m(OiEi)2Ei(3) \chi^2 = \sum_{i=1}^{m} \frac {(O_i-E_i)^2}{E_i} \tag{3}

    1. 기댓값을 파악한다.
    2. 관찰값과 기댓값의 차이를 구하여 제곱하고 기댓값으로 나눔으로써 정규화를 한다.
    3. 각각의 관찰 속성에 대하여 위와 같이 구한 값을 더한다.

    모든 관찰값이 5 이상이면 카이제곱 분포 식 (1)에 근사한다고 증명되어 있다.

    • 관찰값이 5보다 작은 경우라면 카이제곱이 아니라 피셔의 정확 검증 시도

    카이제곱 검정

    예를 들어 자유도 kk인 카이제곱 분포에 대해 아래와 같이 검정을 수행한다.

    1. 유의수준 α\alpha를 정한다.
      • 예: α=0.05\alpha = 0.05
    2. 자유도 kk인 확률변수의 관찰값들에 대하여 카이제곱 값 QQ를 구한다.
      • 예: k=2k = 2 => Q=6.1Q = 6.1
    3. 카이제곱 분포표에서 자유도 kk인 경우 유의수준 α\alpha에 해당하는 카이제곱 값 χ2\chi^2을 찾는다.
      • 예: k=2,α=0.05k = 2, \alpha = 0.05 => χ2=5.99\chi^2 = 5.99
    4. 계산한 카이제곱 값이 카이제곱 분포표에서 찾은 값보다 크면 귀무가설을 기각하고 대립가설을 채택한다.
      • 예: Q>χ2Q > \chi^2 => 차이가 유의하므로 귀무가설 기각, 대립가설 채택

    참고 자료

    Written with StackEdit.

    2024년 7월 24일 수요일

    독일 V2 로켓과 런던 폭격 - 푸아송 분포와 카이제곱 검정

    독일 V2 로켓과 런던 폭격 - 푸아송 분포와 카이제곱 검정

    아래 자료들을 참고하면서 실습을 하였습니다.

    푸아송 분포

    실제값

    n_squares = 576
    n_bombs = 537
    
    # 구역당 떨어진 폭탄 수의 평균
    m = n_bombs / n_squares
    print(f'{m:.3f}')
    
    0.932
    
    # 구역당 떨어진 폭탄 수
    n_bombs_per_square = [0, 1, 2, 3, 4, 5]
    
    # 해당 구역 수 (관찰도수)
    observed_num_of_squares = [229, 211, 93, 35, 7, 1]
    
    print(f'# of squares: {sum(observed_num_of_squares)}')
    
    # of squares: 576
    

    기댓값

    import math
    
    def poisson_distribution(m, k):
        return pow(m, k) / math.factorial(k) * pow(math.e, -m)
    
    # 푸아송 분포에 따른 기대 구역 수 (기대도수)
    expected_num_of_squares = [n_squares * poisson_distribution(m, k) for k in n_bombs_per_square]
    print([round(v, 2) for v in expected_num_of_squares])
    
    n_expected_squares = sum(expected_num_of_squares)
    expected_num_of_squares[-1] += n_squares - n_expected_squares
    print([round(v, 2) for v in expected_num_of_squares])
    
    print(f'# of squares: {sum(expected_num_of_squares)}')
    
    [226.74, 211.39, 98.54, 30.62, 7.14, 1.33]
    [226.74, 211.39, 98.54, 30.62, 7.14, 1.57]
    # of squares: 576.0
    
    import matplotlib.pyplot as plt
    
    plt.figure(figsize=(10, 6))
    
    plt.plot(n_bombs_per_square, observed_num_of_squares, marker='o', label='observed')
    plt.plot(n_bombs_per_square, expected_num_of_squares, marker='x', label='expected')
    
    plt.xlabel('No. of flying bombs per square')
    plt.ylabel('No. of squares')
    plt.legend()
    plt.grid(True)
    plt.show()
    

    png

    카이제곱(χ2{\chi}^2) 검정

    다음은 AN APPLICATION OF THE POISSON DISTRIBUTION 글의 끝 부분을 인용한 것입니다.

    The occurrence of clustering would have been reflected in the above table by an excess number of squares containing either a high number of flying bombs or none at all, with a deficiency in the intermediate classes. The closeness of fit which in fact appears lends no support to the clustering hypothesis.
    Applying the x2 test to the comparison of actual with expected figures, we obtain x2 = 1.17. There are 4 degrees of freedom, and the probability of obtaining this or a higher value of x2 is .88.

    관찰도수가 특정 분포를 따르는지 여부를 검정하는 것이므로 적합도 검정(Goodness-of-fit Test)에 해당합니다.

    # 피어슨 카이제곱 통계량
    x2 = 0
    for o_num, e_num in zip(observed_num_of_squares, expected_num_of_squares):
        item_val = pow(o_num - e_num, 2) / e_num
        x2 += item_val
        print(f'o_num: {o_num:8.4f}, e_num: {e_num:8.4f} => item_val: {item_val:.4f}')
        
    print(f'** x2: {round(x2, 2)}')
    
    o_num: 229.0000, e_num: 226.7427 => item_val: 0.0225
    o_num: 211.0000, e_num: 211.3904 => item_val: 0.0007
    o_num:  93.0000, e_num:  98.5387 => item_val: 0.3113
    o_num:  35.0000, e_num:  30.6223 => item_val: 0.6258
    o_num:   7.0000, e_num:   7.1372 => item_val: 0.0026
    o_num:   1.0000, e_num:   1.5687 => item_val: 0.2062
    ** x2: 1.17
    

    값의 종류는 6 개이고 관찰값으로부터 파라미터 mm을 추정하였으므로 자유도 kk는 다음과 같습니다.

    k=(61)1=4 k = (6 - 1) - 1 = 4

    카이제곱 분포표에 따르면 자유도가 4이고 유의수준 5%에 해당하는 χ2\chi^2 값은 9.49입니다. 위에서 구한 카이제곱 값이 이보다 작으므로 귀무가설을 채택합니다.

    • 귀무가설: 관찰도수는 푸아송 분포를 따른다.
    • 대립가설: 관찰도수는 푸아송 분포를 따르지 않는다.

    참고

    • 큰 수의 법칙, 나무위키

      때문에 책 <이공계의 뇌로 산다>에서 소개된 노벨경제학상 수상자 대니얼 카너먼은 우스갯소리로 이러한 불충분한 표본으로 잘못 만들어진 p 값으로 인한 '잘못된 큰 수의 법칙 적용 사례’를 두고 '작은 수의 법칙’이라고 얘기했다고 한다. 즉 실제 있는 법칙은 아니다. 또한 잘못된 p 값 예시로 V2의 런던폭격분포를 예로 들었다.

    Written with StackEdit.

    2024년 7월 23일 화요일

    독일 V2 로켓과 런던 폭격 - 푸아송 분포 응용

    독일 V2 로켓과 런던 폭격 - 푸아송 분포 응용

    관심을 가졌던 사항은 2차 세계 대전 말기 독일 V2 로켓에 의한 런던 폭격 지점의 분포와 푸아송 분포 응용 사례였는데 우연히 접한 기사를 흥미있게 읽다가 오류로 의심되는 부분을 발견하고 이를 확인하는 과정에서 글을 쓰게 되었습니다.

    기사 인용

    • [문학이 사랑한 통계⑬] 로저 멕시코와 함께하는 확률론 강의, 2019.01.03, 시사위크

      • 도서 내용 중 일부 - 토머스 핀천, 중력의 무지개, 이상국 옮김, 새물결

        로저는 그녀에게 V폭탄의 통계에 대해 애써 설명했다. 천사의 눈에나 보일 영국 지도 안의 분포와 이 아래 인간의 눈에 보이는, 그들 자신이 살아남을 기회의 차이에 대해.

        “그러나 이미 폭격을 몇 번이나 받은 곳도 있잖아. 그러니까-” “미안하지만 그게 바로 몬테카를로 오류라는 거야. 특정한 지역에 얼마나 많이 떨어졌든 미래의 확률은 차이가 없어. 로켓들은 각자, 서로와 아무 관계없이 떨어져. 폭탄은 개가 아니야. 관계도 모르고, 기억도 없어. 적응이란 것도 없어.” 교과서의 푸아송 공식으로 예측되는 분포 그대로, 정말로 로켓들은 런던 전역에 떨어진다.

      • 기사 내용 중 일부

        런던 대공습에 나선 독일의 폭격기 조종사들은 과연 폭탄을 떨어트릴 때 분명한 목표물을 갖고 있었을까. 이 주제에 대해선 실제로 연구가 진행된 바 있다. 2차 세계대전 당시 영국 통계학자 R.D.클라크는 런던 남부를 576개(24_24) 구역으로 나누고, 각각의 구역에 떨어진 *_폭탄의 개수를 세는 방식으로 통계검정을 진행했다. 만약 독일 공군이 특별한 타깃 없이 무작위로 폭탄을 떨어트렸다면(평균확률밀도가 일정하다면) 구역별로 폭탄이 떨어진 횟수의 분포는 푸아송분포를 따를 것이다.

    확인이 필요한 사항

    위 글에서 다음 두 가지 사항이 맞는지 확인해 볼 필요가 있습니다.

    • 도서 내용 중

      • 로저가 V폭탄의 통계에 대해 설명했다고 하는데 V1, V2 로켓과는 다른 V폭탄이란 것이 있었던 걸까? 폭탄과 로켓은 다르니까. (참고: 현대 무기 분류에 따르면 로켓이 아니라 미사일에 해당한다고 하지만 과거에는 로켓이라고 불렀음)
    • 기사 내용 중

      • 폭격기 조종사들이 떨어트리는 폭탄에 이어서 R.D.클라크의 연구를 언급했다. 그런데 R.D.클라크가 연구에 사용한 데이터가 2차 대전 초기 런던 대공습 당시에 독일 공군이 투하한 폭탄이 떨어진 지점에 대한 것인지, 아니면 2차 대전 말기 독일이 개발해서 원거리에서 발사한 V2 로켓이 떨어진 지점에 대한 것인지?

    도서 내용의 오류 여부

    우선 V폭탄이란 것이 있었는지부터 살펴 보겠습니다.

    • V1(로켓), 나무위키

      Vergeltungswaffe 1(보복병기 1호)는 제2차 세계 대전 당시 나치 독일의 '피젤러’사에서 개발한 무기이다. 약자인 V-1, V1은 아돌프 히틀러가 선전 선동 목적으로 붙인 이름이다.

      당시에는 순항 미사일이라는 개념이 없었기 때문에 '비행폭탄’이라고 불리기도 했다. 폭탄(850kg)이 든 시가형 동체에 작은 날개와 펄스제트 엔진이 붙어있는 형태로, 세계 최초의 제트 추진 순항 미사일로 평가된다. 다만, 나온 시기가 시기이다 보니 앞에 작은 프로펠러를 달아 그것이 돌아간 숫자로 거리를 재서 목표 상공에서 연료 공급을 끊어 추락하는 방식이라 도시 정도 크기의 목표를 향한 무차별 폭격만이 가능했다.

    "2차 대전 독일 V폭탄"으로 검색하면 V1(로켓), V2(로켓)이 나오므로 V폭탄이란 모델이 따로 있었던 것은 아닌 것으로 보입니다. V폭탄이 V1, V2를 지칭한다고 하면 이것들을 만들었을 당시에 '비행폭탄(flying bombs)'이라고 불렀으므로 폭탄이라는 용어를 쓰는 것이 틀렸다고 말하기는 어렵고 더군다나 소설의 배경에 해당하는 시점에서는 자연스러운 용어일 수도 있겠습니다.

    기사 내용의 오류 여부

    • Poisson distribution, Britannica

      The Poisson distribution is now recognized as a vitally important distribution in its own right. For example, in 1946 the British statistician R.D. Clarke published “An Application of the Poisson Distribution,” in which he disclosed his analysis of the distribution of hits of flying bombs (V-1 and V-2 missiles) in London during World War II. Some areas were hit more often than others. The British military wished to know if the Germans were targeting these districts (the hits indicating great technical precision) or if the distribution was due to chance. If the missiles were in fact only randomly targeted (within a more general area), the British could simply disperse important installations to decrease the likelihood of their being hit.

    이제 위의 브리태니커 사전 내용을 살펴 보면 다음 두 가지 사항이 명확해집니다.

    • 비행폭탄이라고 쓰고 괄호 속에 V-1, V-2 미사일을 덧붙였습니다.
    • R.D.클라크가 연구한 데이터는 V-1, V-2 미사일 폭격 지점의 분포입니다.

    따라서 기사에서 발견된 오류는 다음과 같습니다.

    • 런던 대공습에 나선 독일의 폭격기 조종사들은 과연 폭탄을 떨어트릴 때 분명한 목표물을 갖고 있었을까.

    위 문장 하나만 놓고 보면 오류가 아니지만 인용한 도서의 내용과 기사의 내용이 2차 대전 말기 독일의 미사일 공격을 대상으로 하고 있고 2차 대전 초기의 런던 대공습이나 폭격기 조종사들이 떨어트린 폭탄과는 무관하므로 문맥상 오류라고 볼 수 있겠습니다.

    푸아송 분포 응용 목적

    기사의 오류 여부를 확인하다가 뜻하지 않게 브리태니커 사전으로부터 R.D.클라크가 수행한 푸아송 분포 응용의 목적이 무엇이었는지 알게 되었습니다.

    • 독일 미사일의 공격이 정밀하다면 영국군은 주요 시설 보호에 초점을 맞추어야 하고 그렇지 않다면 주요 시설들을 이곳 저곳 분산해서 배치하는 것만으로도 타격 위험을 줄일 수 있기 때문입니다.

    더 읽어 볼 만한 자료

    Written with StackEdit.

    JWT 토큰 생성과 유효성 확인 과정

    JWT 토큰 생성과 유효성 확인 과정 API 서비스를 개발하고 이에 대한 접근 권한을 제어하기 위하여 JSON Web Token(JWT)을 활용할 수 있습니다. 이 문서에서는 JWT 토큰의 생성과 유효성 확인 과정...