728x90
반응형

Machine Learning

데이터 수집부터 머신러닝까지 순서

Pipeline

 

1. Data 수집하기

- Own datasets (csv, pickle등 본인이 수집한 데이터 혹은 기업 데이터/개인이거나 단체가 직접 수집한 데이터)

- Public datasets (공공데이터 API, mnist, kaggle data )

- Crawling & Scraping (거의 대부분이 불법이니 조심해야 한다)

- 기타

2. 도메인 지식 확보하기 (필요할 )

- 데이터에 포함된 Documentation을 읽는다

- 이미 알고 있는 전문 분야일 경우 생략

- 구글링

-

- 기타

3. Data 불러오기

- Pandas DataFrame으로 불러오기

- Numpy로 불러오기

- 기타

4. 대략적인 Data 파악하기

- Info로 데이터 정보 확인하기

- dtype확인 하기

- shape확인 하기

- missing data확인 하기

- 마지막 Tidy data인지 확인 하기

5. Data 조작하기 (Tidy data가 아닐 경우)

- Wide Format인 경우 Long Format으로 바꾸기

- missing data 처리하기

- 부적절한 data값 수정하기

- 기타

6. Data 분석하기(EDA)

- 그래프 그려보기 (histogram, pairplot, bar, pie등)

- 데이터의 숨겨진 의미 파악하기

- 데이터 양이 충분한지 파악하기 => learning_curve를 통해 확인한다 (중심 극한 정리)
- Feature 갯수가 데이터의 양에 비해 많은지 확인하기 (오캄의 면도날)

- 기타

7. Data 전처리

- Hold out (2 way/3 way)

- Feature Selection

- Feature Scaling

- Dimensionality Reduction

- 기타

8. 모델링 하기

- Model selection

- 알고리즘 비교하기

- 하이퍼 파라미터 찾기(GridSearchCV)

- 기타

9. 성능 테스트

- 데이터 양이 적을 경우 Cross validation

- 정확도 파악하기

- 기타

 

Data 형태

머신 러닝의 기본 데이터 형은 정형 데이터이다

정형 데이터는 2차원 형태의 데이터이다

row(행)가 한 개이고 vector형태의 구조여야 한다

column(열)은 feature또는 dimension이라고 부른다

최종적으로 Tidy data 형태로 만들어줘야 한다

EDA(Exploratory Data Analysis)

데이터를 다양한 각도에서 관찰하고 이해하는 과정이다

데이터를 분석하기 전에 그래프나 통계적인 방법으로 자료를 직관적으로 확인하는 과정이라고도 할 수 있다

Data shift

학습데이터셋과 테스트셋의 분포가 다른 경우

Imbalanced data

클래스가 불균등한 데이터

ex) True가 100만개 False가 100개

기초 통계 분석(EDA)시 알아두면 좋은 원칙

1. Law of large numbers( 수의 법칙)

- 모집단에서 무작위로 뽑은 표본의 평균이 전체 모집단의 평균과 가까울 가능성이 높다

- 모집단이 커지면 표본평균은 모평균을 정확히 추정할 있다

2. Occam's Razor (오캄의 면도날)

- 같은 성능을 보일 간단한것을 택한다

3. Curse of dimensionality (차원의 저주)

- 차원이 커지면 커질수록 필요한 데이터의 양이 커져야 한다

4. Central limit theorem (중심 극한 정리)

- 동일한 확률분포를 가진 독립 확률 변수 n개의 평균의 분포는 n이 적당히 크다면 정규분포에 가까워진다는 정리

 

from sklearn.model_selection import learning_curve # 데이터의 많고 적음은 learning_curve를 보고 확인한다 
import numpy as np
import tensorflow as tf 

(X_train, y_train), (X_test, y_test) = tf.keras.datasets.mnist.load_data()

X_test.shape
# (10000, 28, 28)

y_test.shape
# (10000,)

Train-Test Ratio

Holdout 비율 잡는 방법 => 경험

보통은 75%/25% 비율로 한다

import matplotlib.pyplot as plt
plt.hist(y_train); # 균형 잡힌 train 데이터

np.unique(y_train, return_counts=True) # 클래스 종류 10가지 
# (array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=uint8),
# array([5923, 6742, 5958, 6131, 5842, 5421, 5918, 6265, 5851, 5949]))

from sklearn.datasets import load_iris
from sklearn.neighbors import KNeighborsClassifier
from sklearn_evaluation import plot
# ImportError: cannot import name 'TypeGuard' from 'typing_extensions' (/usr/local/lib/python3.7/dist-packages/typing_extensions.py)
!pip install -U sklearn-evaluation
data = load_iris()
train_size, train_score, test_score = learning_curve(KNeighborsClassifier(), data.data, data.target)
plot.learning_curve(train_score, test_score, train_size);

Holdout

Train data, test data를 분리시키는 방법

1. 2-way => train-test

2. 3-way => train-validation-test

from sklearn.model_selection import train_test_split # 2-way / 3-way는 train_test_split을 두 번 하면 된다

Modeling

모델이란 수많은 데이터를 통해 패턴을 발견하거나 예측을 수행하는 알고리즘의 표현식이다

따라서 모델링은 그러한 알고리즘을 만들어 내는 과정을 말한다

모델을 잘 만들기 위해서 모델링하기 전 데이터에 대한 이해가 필요하다 => EDA를 한다

모델링 하기 전에 알아둬야 할 키워드

1. Ad-hoc : 특별한 목적을 가진 것

- 데이터에 따라서 달라진다

- 일반적이지 않다

2. No free lunch <----> Master algorithm

- 공짜 점심은 없다

from sklearn.model_selection import cross_val_score # 데이터 양이 적을때 사용하는 방법 
re # 데이터 양이 적을때 사용하는 방법 
model = tf.keras.Sequential([
    tf.keras.layers.Flatten(input_shape = (28,28)) # (28,28)인 2차원으로 만들어 준다 
])

X_train[0].shape # 2차원 데이터 
# (28, 28)

X_train.dtype
# dtype('uint8')

data = model(X_train) # 묵시적으로 데이터 타입이 uint8에서 float32로 바뀌었다 / 미분해야 하기 때문에 float타입이어야 한다 
model.fit() # class_weight => 클래스에 가중치를 줌으로써 불균등한 데이터의 문제를 해결할 수 있다

from sklearn.linear_model import LogisticRegression
from sklearn.multiclass import OneVsOneClassifier, OneVsRestClassifier
from sklearn.svm import SVC

Ir = LogisticRegression() # 가정이 성립되면 전통적인 머신러닝 알고리즘 사용 가능하다 
Ir.fit(data.numpy(), y_train)
Ir.predict(X_test[0].reshape(1,-1))
y_test[0]
# 7

svm = SVC()
svm.fit(data.numpy(), y_train)

 

본 수업 용어 규칙

전통적인 머신러닝 모델 = end to end (feature extraction + model)이 아닌 모델

여기서 end to end 모델은 input이 들어가서 output이 나오는 과정을 오로지 데이터에 의존해서 찾아내는 모델을 말한다

end to end 모델이 성능이 좋을때가 많지만 충분한 학습 데이터가 없다면 굉장히 성능이 떨어지는 모델이 될 수 있다

그리고 end to end 모델은 중간 과정을 설명할 수 없기 때문에 중간 과정을 설명해야 하는 중요한 문제가 발생하는 부분에서는 사용하기가 쉽지 않다

 

i.i.d (independent identically distributed)

독립 항등 분포 column을 바꿔도 의미가 유지가 되어야 한다

import seaborn as sns
iris = sns.load_dataset('iris')

이미지는 column이 바뀌면 의미가 유지되지 않는다

그래서 전통적인 방식의 머신러닝으로 이미지 데이터 사용하기 쉽지 않다

단, 가설(가정)을 통해서 가능하게 할 수 있다

 

Assumption

이미지 데이터를 사용하여 전통적인 머신러닝 방식 해결하려면 가정이 필요하다

가정이 잘못 되면 모든 것이 잘 못 될 경우가 있다

이미지 데이터를 사용할 경우 data leakage문제가 발생하지 않는다 라는 가정을 갖고 학습을 하게 되면 전통적인 머신러닝 방식으로도 사용할 수 있다

가정을 많이 할 수록 사용할 수 있는 것이 한정적이게 된다

data leakage 문제: 학습 데이터 밖에서 유입된 데이터가 모델을 만드는데 사용되어 overfitting이 되거나 underfitting이 되는 경우를 말한다

 

데이터를 2차원에서 1차원으로 변형할때 context를 유지해야 한다

!pip install mglearn
import mglearn
mglearn.plot_pca.plot_pca_illustration() # 1차원으로 바꿔도 context가 유지되는 경우

mglearn.plot_scaling.plot_scaling() # 정규화, 표준화 등 scaling을 해도 context가 유지된 경우

Model Selection

1. Algorithm(알고리즘) 선택

2. Hyper parameter(하이퍼 파라미터) 선택

3. Ensemble(앙상블)

4. Train + Test data 합쳐서 재학습 => 최종 모델

 

모델별로 inductive bias가 있다

inductive bias란 학습 시에는 만나보지 않았던 상황에 대하여 정확한 예측을 하기 위해 사용하는 추가적인 가정 (additional assumptions)을 의미한다

이러한 inductive bias를 추가하여 학습한 모델을 일반화 하는 것이 모델 학습의 최종 목표이다

opencv에도 알고리즘이 있다

import cv2
dir(cv2.ml) # 머신러닝 알고리즘
['ANN_MLP_ANNEAL',
 'ANN_MLP_BACKPROP',
 'ANN_MLP_GAUSSIAN',
 'ANN_MLP_IDENTITY',
 'ANN_MLP_LEAKYRELU',
 'ANN_MLP_NO_INPUT_SCALE',
 'ANN_MLP_NO_OUTPUT_SCALE',
 'ANN_MLP_RELU',
 'ANN_MLP_RPROP',
 'ANN_MLP_SIGMOID_SYM',
 'ANN_MLP_UPDATE_WEIGHTS',
 'ANN_MLP_create',
 'ANN_MLP_load',
 'BOOST_DISCRETE',
 
 
 dir(cv2.dnn) # 딥 뉴럴 네트워크
 ['DNN_BACKEND_DEFAULT',
 'DNN_BACKEND_HALIDE',
 'DNN_BACKEND_INFERENCE_ENGINE',
 'DNN_BACKEND_OPENCV',
 'DNN_BACKEND_VKCOM',
 'DNN_TARGET_CPU',
 'DNN_TARGET_FPGA',
 'DNN_TARGET_MYRIAD',
 'DNN_TARGET_OPENCL',
 'DNN_TARGET_OPENCL_FP16',
 'DNN_TARGET_VULKAN',
 'NMSBoxes',
 'NMSBoxesRotated',

 

 

반응형
728x90
반응형

OpenCV

Hisogram

이미지 데이터를 분석하기 위한 방법으로 사용한다

import cv2
import numpy as np

im = cv2.imread('people.jpg')
cv2.calcHist([im],[0], None, [256], [0,256]) # 여러개 동시에 연산하기 때문에 2차로 결과를 낸다 
cv2.calcHist([im,im],[0], None, [256], [0,256])

# 색상 분포
plt.plot(cv2.calcHist([im],[0], None, [256], [0,256])) # B
plt.plot(cv2.calcHist([im],[1], None, [256], [0,256])) # G
plt.plot(cv2.calcHist([im],[2], None, [256], [0,256])) # R 
plt.xlim([0, 256])
# (0.0, 256.0)

squeeze

길이가 1 인 축을 제거한다

a = np.array([[1],[3]])
b = np.array([[[1,2],[3,4]]])

a.shape, b.shape
# ((2, 1), (1, 2, 2))

np.squeeze(a).shape, np.squeeze(b).shape
# ((2,), (2, 2))

cv2.calcHist([im,im],[0], None, [256], [0,256]).shape
# (256, 1)

np.squeeze(cv2.calcHist([im,im],[0], None, [256], [0,256])).shape
# (256,)

plt.plot(cv2.calcHist([im,im],[0], None, [256], [0,256]))
plt.plot(cv2.calcHist([im,im],[1], None, [256], [0,256]))
plt.plot(cv2.calcHist([im,im],[2], None, [256], [0,256]))

속도 비교

opencv는 속도 최적화가 되어 있다

%timeit im*im
# 1000 loops, best of 5: 202 µs per loop

%timeit im**2
# 1000 loops, best of 5: 227 µs per loop

cv2.useOptimized() # opencv연산에 최적화 되어 있다
# True

%timeit cv2.medianBlur(im, 49) # 최적화 되어 있을 때 (최적화 되어 있을 때 일반적으로 20% 빠르다 / colab에서 medianBlur연산은 최적화 해도 그렇게 빠르지 않다)
# 10 loops, best of 5: 37.5 ms per loop

cv2.setUseOptimized(False)
cv2.useOptimized()
# False

%timeit cv2.medianBlur(im, 49) # 최적화 되지 않을 때 
# 10 loops, best of 5: 37.8 ms per loop

cv2.setUseOptimized(True)
cv2.useOptimized()
# True

%timeit im*im*im 
# The slowest run took 20.40 times longer than the fastest. This could mean that an intermediate result is being cached.
# 1000 loops, best of 5: 292 µs per loop

%timeit im**3 # 연산량이 많아지면서 im*im*im연산보다 im**3이 훨씬 느려졌다 
# 100 loops, best of 5: 3.24 ms per loop

EqualizeHist

im = cv2.imread('people.jpg', 0)
im2 = cv2.equalizeHist(im)

plt.hist(im2.ravel(), 256,[0,256]); # 정규 분포 처럼 분포를 균일하게 만들어 준다

plt.imshow(cv2.equalizeHist(im), cmap='gray') # 평탄화 => contrast가 낮아진다 => 분포가 균일해진다 => 밝고 어두운 정도가 구분하기 힘들어진다

from skimage.exposure import cumulative_distribution
len(cumulative_distribution(im))
# 2

y, x = cumulative_distribution(im)
plt.hist(y);
# (array([ 26.,  29.,  48.,  28.,  17.,  13.,  12.,  13.,  16.,  54.]),
#  array([  2.74348422e-05,   1.00024691e-01,   2.00021948e-01,
#           3.00019204e-01,   4.00016461e-01,   5.00013717e-01,
#           6.00010974e-01,   7.00008230e-01,   8.00005487e-01,
#           9.00002743e-01,   1.00000000e+00]),
#  <a list of 10 Patch objects>)

plt.plot(x,y) # 누적 분포

Numpy

Histogram

import matplotlib.pyplot as plt
im = cv2.imread('people.jpg') # color 
np.histogram(im, bins=10) # return이 두 개 (hist array, bin_edges(length(hist)+1) 
# (array([ 78609,  83722,  55425,  62639,  96251, 126726, 146684, 112897, 90637,  21210]),
# array([   0. ,   25.5,   51. ,   76.5,  102. ,  127.5,  153. ,  178.5, 204. ,  229.5,  255. ]))

im.dtype # 2^8 = 256 
# dtype('uint8')
np.histogram(im, bins=256, range=[0,256]) # 256등분 / 0이 92개 1이 51개... / color정보이기 때문에 수치가 많이 나온다 
(array([  92,   51,  119,  224,  406,  664, 1290, 1818, 2323, 2826, 3018,
        3290, 3549, 3736, 3856, 3928, 4209, 4413, 4537, 4848, 4852, 4987,
        5005, 4958, 4881, 4729, 4570, 4256, 4186, 3976, 3849, 3605, 3503,
        3489, 3487, 3281, 3249, 3110, 3217, 3150, 3008, 3073, 3052, 3206,
        3193, 3086, 3089, 2943, 2895, 2695, 2554, 2507, 2258, 2389, 2191,
        2185, 2115, 2019, 2163, 2048, 2012, 1996, 2055, 2095, 2010, 2032,
        2135, 2132, 2138, 2077, 2140, 2097, 2144, 2098, 2128, 2106, 2155,
        2202, 2166, 2168, 2230, 2255, 2285, 2306, 2289, 2345, 2337, 2360,
        2358, 2429, 2430, 2487, 2562, 2697, 2688, 2753, 2745, 2776, 2783,
        2924, 2969, 3095, 3149, 3179, 3199, 3258, 3302, 3349, 3284, 3369,
        3413, 3501, 3640, 3540, 3542, 3759, 3692, 3722, 3801, 3801, 3971,
        3999, 4021, 4278, 4239, 4254, 4480, 4509, 4408, 4414, 4677, 4674,
        4743, 4873, 5055, 5089, 5088, 5225, 5319, 5080, 5087, 5054, 5099,
        5108, 4934, 5152, 5206, 5158, 5211, 5364, 5454, 5661, 5593, 5789,
        5680, 5836, 6015, 6139, 6026, 6087, 6087, 6021, 5803, 5809, 5856,
        5577, 5669, 5457, 5222, 5190, 5102, 5128, 5095, 5237, 5525, 5453,
        5417, 5741, 5723, 5894, 5497, 5167, 5182, 4995, 5002, 4729, 4781,
        4483, 4499, 4386, 4289, 4507, 4544, 4808, 4740, 4456, 4135, 4062,
        4144, 3972, 3651, 3572, 3636, 3766, 3982, 4385, 4202, 3822, 3792,
        3704, 3515, 3518, 3573, 3704, 3808, 4347, 3945, 3734, 3859, 3718,
        3615, 3450, 3275, 2991, 3032, 2811, 2796, 2627, 2314, 2118, 2154,
        1870, 1627, 1593, 1576, 1468, 1315, 1319, 1145, 1057,  959,  866,
         745,  614,  560,  469,  383,  319,  242,  217,  175,  144,  105,
          70,   53,  165]),
 array([   0.,    1.,    2.,    3.,    4.,    5.,    6.,    7.,    8.,
           9.,   10.,   11.,   12.,   13.,   14.,   15.,   16.,   17.,
          18.,   19.,   20.,   21.,   22.,   23.,   24.,   25.,   26.,
          27.,   28.,   29.,   30.,   31.,   32.,   33.,   34.,   35.,
          36.,   37.,   38.,   39.,   40.,   41.,   42.,   43.,   44.,
          45.,   46.,   47.,   48.,   49.,   50.,   51.,   52.,   53.,
          54.,   55.,   56.,   57.,   58.,   59.,   60.,   61.,   62.,
          63.,   64.,   65.,   66.,   67.,   68.,   69.,   70.,   71.,
          72.,   73.,   74.,   75.,   76.,   77.,   78.,   79.,   80.,
          81.,   82.,   83.,   84.,   85.,   86.,   87.,   88.,   89.,
          90.,   91.,   92.,   93.,   94.,   95.,   96.,   97.,   98.,
          99.,  100.,  101.,  102.,  103.,  104.,  105.,  106.,  107.,
         108.,  109.,  110.,  111.,  112.,  113.,  114.,  115.,  116.,
         117.,  118.,  119.,  120.,  121.,  122.,  123.,  124.,  125.,
         126.,  127.,  128.,  129.,  130.,  131.,  132.,  133.,  134.,
         135.,  136.,  137.,  138.,  139.,  140.,  141.,  142.,  143.,
         144.,  145.,  146.,  147.,  148.,  149.,  150.,  151.,  152.,
         153.,  154.,  155.,  156.,  157.,  158.,  159.,  160.,  161.,
         162.,  163.,  164.,  165.,  166.,  167.,  168.,  169.,  170.,
         171.,  172.,  173.,  174.,  175.,  176.,  177.,  178.,  179.,
         180.,  181.,  182.,  183.,  184.,  185.,  186.,  187.,  188.,
         189.,  190.,  191.,  192.,  193.,  194.,  195.,  196.,  197.,
         198.,  199.,  200.,  201.,  202.,  203.,  204.,  205.,  206.,
         207.,  208.,  209.,  210.,  211.,  212.,  213.,  214.,  215.,
         216.,  217.,  218.,  219.,  220.,  221.,  222.,  223.,  224.,
         225.,  226.,  227.,  228.,  229.,  230.,  231.,  232.,  233.,
         234.,  235.,  236.,  237.,  238.,  239.,  240.,  241.,  242.,
         243.,  244.,  245.,  246.,  247.,  248.,  249.,  250.,  251.,
         252.,  253.,  254.,  255.,  256.]))
np.histogram(im.flatten(), 256, [0,256]) # flatten함수는 일차원으로 바꿔주는 함수 => 연산을 더 빠르게 하기 위해서 바꾼다 
(array([  92,   51,  119,  224,  406,  664, 1290, 1818, 2323, 2826, 3018,
        3290, 3549, 3736, 3856, 3928, 4209, 4413, 4537, 4848, 4852, 4987,
        5005, 4958, 4881, 4729, 4570, 4256, 4186, 3976, 3849, 3605, 3503,
        3489, 3487, 3281, 3249, 3110, 3217, 3150, 3008, 3073, 3052, 3206,
        3193, 3086, 3089, 2943, 2895, 2695, 2554, 2507, 2258, 2389, 2191,
        2185, 2115, 2019, 2163, 2048, 2012, 1996, 2055, 2095, 2010, 2032,
        2135, 2132, 2138, 2077, 2140, 2097, 2144, 2098, 2128, 2106, 2155,
        2202, 2166, 2168, 2230, 2255, 2285, 2306, 2289, 2345, 2337, 2360,
        2358, 2429, 2430, 2487, 2562, 2697, 2688, 2753, 2745, 2776, 2783,
        2924, 2969, 3095, 3149, 3179, 3199, 3258, 3302, 3349, 3284, 3369,
        3413, 3501, 3640, 3540, 3542, 3759, 3692, 3722, 3801, 3801, 3971,
        3999, 4021, 4278, 4239, 4254, 4480, 4509, 4408, 4414, 4677, 4674,
        4743, 4873, 5055, 5089, 5088, 5225, 5319, 5080, 5087, 5054, 5099,
        5108, 4934, 5152, 5206, 5158, 5211, 5364, 5454, 5661, 5593, 5789,
        5680, 5836, 6015, 6139, 6026, 6087, 6087, 6021, 5803, 5809, 5856,
        5577, 5669, 5457, 5222, 5190, 5102, 5128, 5095, 5237, 5525, 5453,
        5417, 5741, 5723, 5894, 5497, 5167, 5182, 4995, 5002, 4729, 4781,
        4483, 4499, 4386, 4289, 4507, 4544, 4808, 4740, 4456, 4135, 4062,
        4144, 3972, 3651, 3572, 3636, 3766, 3982, 4385, 4202, 3822, 3792,
        3704, 3515, 3518, 3573, 3704, 3808, 4347, 3945, 3734, 3859, 3718,
        3615, 3450, 3275, 2991, 3032, 2811, 2796, 2627, 2314, 2118, 2154,
        1870, 1627, 1593, 1576, 1468, 1315, 1319, 1145, 1057,  959,  866,
         745,  614,  560,  469,  383,  319,  242,  217,  175,  144,  105,
          70,   53,  165]),
 array([   0.,    1.,    2.,    3.,    4.,    5.,    6.,    7.,    8.,
           9.,   10.,   11.,   12.,   13.,   14.,   15.,   16.,   17.,
          18.,   19.,   20.,   21.,   22.,   23.,   24.,   25.,   26.,
          27.,   28.,   29.,   30.,   31.,   32.,   33.,   34.,   35.,
          36.,   37.,   38.,   39.,   40.,   41.,   42.,   43.,   44.,
          45.,   46.,   47.,   48.,   49.,   50.,   51.,   52.,   53.,
          54.,   55.,   56.,   57.,   58.,   59.,   60.,   61.,   62.,
          63.,   64.,   65.,   66.,   67.,   68.,   69.,   70.,   71.,
          72.,   73.,   74.,   75.,   76.,   77.,   78.,   79.,   80.,
          81.,   82.,   83.,   84.,   85.,   86.,   87.,   88.,   89.,
          90.,   91.,   92.,   93.,   94.,   95.,   96.,   97.,   98.,
          99.,  100.,  101.,  102.,  103.,  104.,  105.,  106.,  107.,
         108.,  109.,  110.,  111.,  112.,  113.,  114.,  115.,  116.,
         117.,  118.,  119.,  120.,  121.,  122.,  123.,  124.,  125.,
         126.,  127.,  128.,  129.,  130.,  131.,  132.,  133.,  134.,
         135.,  136.,  137.,  138.,  139.,  140.,  141.,  142.,  143.,
         144.,  145.,  146.,  147.,  148.,  149.,  150.,  151.,  152.,
         153.,  154.,  155.,  156.,  157.,  158.,  159.,  160.,  161.,
         162.,  163.,  164.,  165.,  166.,  167.,  168.,  169.,  170.,
         171.,  172.,  173.,  174.,  175.,  176.,  177.,  178.,  179.,
         180.,  181.,  182.,  183.,  184.,  185.,  186.,  187.,  188.,
         189.,  190.,  191.,  192.,  193.,  194.,  195.,  196.,  197.,
         198.,  199.,  200.,  201.,  202.,  203.,  204.,  205.,  206.,
         207.,  208.,  209.,  210.,  211.,  212.,  213.,  214.,  215.,
         216.,  217.,  218.,  219.,  220.,  221.,  222.,  223.,  224.,
         225.,  226.,  227.,  228.,  229.,  230.,  231.,  232.,  233.,
         234.,  235.,  236.,  237.,  238.,  239.,  240.,  241.,  242.,
         243.,  244.,  245.,  246.,  247.,  248.,  249.,  250.,  251.,
         252.,  253.,  254.,  255.,  256.]))

일차원으로 만드는 함수

1. ravel # view 방식

2. flatten # copy 방식

plt.hist(im.ravel(), 256, [0, 256]); # 내부적으로 bincount를 사용해서 연산한다 
plt.xlim([0,256]) # x축 범위 설정

im2 = cv2.imread('people.jpg', 0) # 흑백 
plt.hist(im2.flatten(), 256, [0, 256]);
plt.xlim([0,256])
# (0.0, 256.0)

# 각각 점의 컬러 분포 
plt.hist(im[...,0].ravel(), 256, [0, 256]); # B
plt.hist(im[...,1].ravel(), 256, [0, 256]); # G
plt.hist(im[...,2].ravel(), 256, [0, 256]); # R
plt.xlim([0,256])

 

im2.shape # 흑백 
# (540, 540)
im2
# array([[191, 193, 194, ..., 165, 166, 168],
#       [190, 192, 193, ..., 173, 175, 176],
#       [189, 191, 192, ..., 182, 183, 184],
#       ..., 
#       [106, 102, 109, ..., 113, 124, 129],
#       [106, 102, 109, ..., 113, 120, 123],
#       [101,  97, 104, ..., 119, 120, 120]], dtype=uint8)

plt.hist(im2[0].flatten(), 256, [0, 256]); 
plt.hist(im2[1].flatten(), 256, [0, 256]); 
plt.hist(im2[539].flatten(), 256, [0, 256]); 
plt.xlim([0,256])

np.bincount(np.arange(5)) # 각각 몇개 있는지 세는 함수   
# array([1, 1, 1, 1, 1])

np.bincount(np.array([0,1,1,3,2,1,7]))
# array([1, 3, 1, 1, 0, 0, 0, 1])

from itertools import count, groupby
t = groupby([1,1,1,2,2]) 
b = count()
next(b)
# 3

next(t) # 그룹으로 묶어서 몇개 있는지 확인 
# (2, <itertools._grouper at 0x7fa251080ad0>)

얼굴부분 crop해서 색 분포 확인하기

face = im[50:300,50:200]
plt.imshow(face[...,::-1])

plt.hist(face[...,0].ravel(), 256, [0, 256], color='blue'); 
plt.hist(face[...,1].ravel(), 256, [0, 256], color='green'); 
plt.hist(face[...,2].ravel(), 256, [0, 256], color='red'); 
plt.xlim([0,256])

mask = np.zeros(im.shape[:2], np.uint8) # mask 
mask[50:300,50:200] = 255 # 흰색으로 
cv2.bitwise_and(im,im,mask=mask) 
plt.imshow(cv2.bitwise_and(im,im,mask=mask))

plt.imshow(cv2.bitwise_and(im,mask)) # 차원이 달라서 연산이 안된다 
# error: OpenCV(4.1.2) /io/opencv/modules/core/src/arithm.cpp:229: error: (-209:Sizes of input arguments do not match) The operation is neither 'array op array' (where arrays have the same size and type), nor 'array op scalar', nor 'scalar op array' in function 'binary_op'
im.shape, mask.shape
# ((540, 540, 3), (540, 540))

im2 = cv2.imread('people.jpg', 0)
plt.imshow(cv2.bitwise_and(im2,mask))

cv2.calcHist([im], [0], mask, [256], [0,256]) # mask외 분포 구할 때 
plt.plot(cv2.calcHist([im], [0], mask, [256], [0,256]))

전통적인 머신러닝에서는 contrast가 높으면 이미지 분류가 잘 안되기 때문에 contrast 분포를 평평하게 만들었었다 (contrast normalization)

from skimage.exposure import histogram, rescale_intensity, equalize_hist
from skimage.util import img_as_float, img_as_ubyte 
im = cv2.imread('people.jpg')
plt.imshow(img_as_float(im)[...,::-1])
# 이미지는 float나 int 둘다 사람이 볼때는 상관 없다 / 하지만 컴퓨터는 float로 바꾸면 연속적이기 때문에 미분이 가능해진다

help(histogram)
np.info(histogram)
np.lookfor('hist', 'skimage')
import scipy
np.lookfor('hist', 'scipy')
equalize_hist(im.flatten())
# array([ 0.73654893,  0.80552241,  0.86368427, ...,  0.47972908, 0.42027549,  0.32775834])
plt.imshow(rescale_intensity(im, (0, 255))) # 범위를 조절 해준다

plt.imshow(rescale_intensity(im[...,::-1], (0, 255)))

plt.imshow(rescale_intensity(im[...,::-1], (0, 50))) # 0에서 50사이로 범위를 조절 했기 때문에 밝게 변한다

plt.imshow(rescale_intensity(im[...,::-1], (, 200)))

advanced

x = [1,2,3,4,5,6]
y = np.array(x)

y.cumsum() # 누적합을 빠르게 구하는 방법 
# array([ 1,  3,  6, 10, 15, 21])

np.add.accumulate(y)
# array([ 1,  3,  6, 10, 15, 21])

np.add.reduce(y)
# 21
import tensorflow as tf 
tf.reduce_sum(y)
# <tf.Tensor: shape=(), dtype=int64, numpy=21>

# import seaborn as sns 
iris = sns.load_dataset('iris')
sns.pairplot(iris, hue='species', diag_kind='hist') # 데이터에 분포에 따라서 옳바른 데이터인지 아닌지 확인할 수 있기 때문에 그래프를 확인한다

sns.pairplot(iris, hue='species', diag_kind='kde')

누적 분포

hist, bins = np.histogram(im.ravel(), 256, [0,256])
cdf = hist.cumsum()

cdf_norm = cdf*float(hist.max()) / cdf.max() # 정규화, 0과 1사이의 값으로 만든다 

plt.hist(im.ravel(), 256, [0,256])
plt.plot(cdf_norm)

반응형
728x90
반응형
im[a,b,0] = 255 # R
im[a,b,1] = 0   # G
im[a,b,2] = 0   # B

plt.figure(figsize=(6,6))
plt.imshow(im)

Image Processing

이미지 처리란 넓은 의미에서 광학, 아날로그, 디지털 사진 혹은 영상 처리를 의미하지만,

computer vision에서는 디지털 이미지 데이터를 조작하여 원하는 데이터 형태로 변환하는 것을 의미한다.

이미지 처리 기법

1. 이미지 자르기(crop), 색상 공간(color space)변경, 이미지 깊이(image depth)변경, 확대·축소·회전·뒤집기와 같은 기하학적 변환

2. 이미지 대비·밝기·색상 밸런스 조정·선명도 변경과 같은 변환

3. 이미지 합성, 필터(filter, kernel, mask) 사용한 블러링(Blurring)

4. 팽창과 침식 연산을 사용한 모폴로지(Morphology) 연산

5. 이미지 분할(Segmentation)

6. 이미지 검출(Detection)

7. 자세 추정(Pose Estimation)

8. 이미지 증강(Augmentation) 다양한 기법들이 있다

 

PIL, opencv, scikit-image등으로 이미지 처리 기법을 구현하여 원하는 이미지 형태로 변형 있다.

단순히 python 라이브러리로 이미지 검출이나, 자세 추정, 이미지 분할등 원하는 형태로 이미지를 가공 있지만

머신러닝, 딥러닝을 활용하면 조금 정교하고 대량의 이미지를 짧은 시간에 처리하는 것이 가능해진다.

이미지 처리를 하는 이유

이미지 데이터를 활용하여 어떠한 목적을 달성해야 할때 (머신러닝, 딥러닝을 활용하여 문제 해결을 해야 할때)

충분하지 못한 이미지 데이터를 갖고 있거나, 데이터 품질이 좋지 못할때, 데이터에 표시를 해야 할때등

여러가지 이유에서 목적을 수월하게 달성할 수 있다.

이미지 처리를 위한 python library

1. Scikit-image => Numpy style

2. OpenCV => C style

3. PIL => Python style

Scikit-Image

scikit-learn과 비슷한 명명 규칙을 따른다

 

skimage의 중분류

1. color # 색 변환

2. draw # 이미지내 그림 표시, 문자 표시, 좌표 그리기

from skimage.draw import line, rectangle, circle
from skimage.io import imread
import matplotlib.pyplot as plt
import numpy as np

len(line(0,0,100,100))
# 2
a, b = line(0,0,100,200) # 좌표평면에서 y축 방향은 앞 두 자리(0,0), x축은 뒤 두 자리(100,200) / 왼쪽 위(0,0)좌표에서 오른쪽 아래(100,200)좌표를 향하는 직선 
a
array([  0,   1,   1,   2,   2,   3,   3,   4,   4,   5,   5,   6,   6,
         7,   7,   8,   8,   9,   9,  10,  10,  11,  11,  12,  12,  13,
        13,  14,  14,  15,  15,  16,  16,  17,  17,  18,  18,  19,  19,
        20,  20,  21,  21,  22,  22,  23,  23,  24,  24,  25,  25,  26,
        26,  27,  27,  28,  28,  29,  29,  30,  30,  31,  31,  32,  32,
        33,  33,  34,  34,  35,  35,  36,  36,  37,  37,  38,  38,  39,
        39,  40,  40,  41,  41,  42,  42,  43,  43,  44,  44,  45,  45,
        46,  46,  47,  47,  48,  48,  49,  49,  50,  50,  51,  51,  52,
        52,  53,  53,  54,  54,  55,  55,  56,  56,  57,  57,  58,  58,
        59,  59,  60,  60,  61,  61,  62,  62,  63,  63,  64,  64,  65,
        65,  66,  66,  67,  67,  68,  68,  69,  69,  70,  70,  71,  71,
        72,  72,  73,  73,  74,  74,  75,  75,  76,  76,  77,  77,  78,
        78,  79,  79,  80,  80,  81,  81,  82,  82,  83,  83,  84,  84,
        85,  85,  86,  86,  87,  87,  88,  88,  89,  89,  90,  90,  91,
        91,  92,  92,  93,  93,  94,  94,  95,  95,  96,  96,  97,  97,
        98,  98,  99,  99, 100, 100])
        
b
array([  0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,
        13,  14,  15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,
        26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,
        39,  40,  41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51,
        52,  53,  54,  55,  56,  57,  58,  59,  60,  61,  62,  63,  64,
        65,  66,  67,  68,  69,  70,  71,  72,  73,  74,  75,  76,  77,
        78,  79,  80,  81,  82,  83,  84,  85,  86,  87,  88,  89,  90,
        91,  92,  93,  94,  95,  96,  97,  98,  99, 100, 101, 102, 103,
       104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,
       117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
       130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142,
       143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155,
       156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168,
       169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181,
       182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194,
       195, 196, 197, 198, 199, 200])
im = imread('people.jpg') 
type(im) # ndarray => mutable 
# numpy.ndarray

im[a,b] = 0
plt.figure(figsize=(6,6))
plt.imshow(im)

im = imread('people.jpg') 
type(im) # ndarray => mutable 
# numpy.ndarray

im[a,b] = 0
plt.figure(figsize=(6,6))
plt.imshow(im)

x = np.arange(24).reshape(4,6)
x[[0,1,2],[1,2,3]] # 1, 8, 15 방향의 직선 => a, b = line(0,0,100,200) 와 유사한 인덱싱  
# array([ 1,  8, 15])

x
array([[ 0,  1,  2,  3,  4,  5],
       [ 6,  7,  8,  9, 10, 11],
       [12, 13, 14, 15, 16, 17],
       [18, 19, 20, 21, 22, 23]])
h, w = rectangle((20,20),(100,100))
im[h,w] = 0
# 직선과 직사각형이 이미지에 같이 표시되는 이유는 im 객체가 mutable이기 때문에 line함수와 rectangle함수가 적용된 결과가 누적된다 
plt.figure(figsize=(6,6)) 
plt.imshow(im)

h, w = rectangle((300,300),(100,100))
im[h,w] = 0
plt.figure(figsize=(6,6)) 
plt.imshow(im)

im.shape
#(540, 540, 3)

im[h,w,...]

im[h,w,:] == im[h,w]

im[h,w][:]

im[h,w]

 

im[h,w,0] = 0

im[h,w,1] = 0 == im[h,w] = 0

im[h,w,2] = 0

좌표축 그리는 3총사

1. np.meshgrid

2. np.ogrid

3. np.mgrid

meshgrid

a = np.arange(100)
b = np.arange(100)
h,w = np.meshgrid(a,b)

grid = h + w 

h.shape
# (50, 100)

w.shape
# (50, 100)

grid
array([[  0,   1,   2, ...,  97,  98,  99],
       [  1,   2,   3, ...,  98,  99, 100],
       [  2,   3,   4, ...,  99, 100, 101],
       ...,
       [ 47,  48,  49, ..., 144, 145, 146],
       [ 48,  49,  50, ..., 145, 146, 147],
       [ 49,  50,  51, ..., 146, 147, 148]])
grid.shape
# (50, 100)

plt.figure(figsize=(6,6))
plt.imshow(grid)

a = np.arange(100)
b = np.arange(50)
h,w = np.meshgrid(a,b)
grid = h + w 

plt.figure(figsize=(6,6))
plt.scatter(w,h) # x축, y축 
plt.grid()

ogrid

'__getitem__' in dir(np.ogrid) # getitem이 있으면 대괄호([])를 사용할 수 있다
# True

len(np.ogrid[0:100,0:100])
# 2


x, y = np.ogrid[0:100,0:100]
x

y
array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
        16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
        32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
        48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
        64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
        80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
        96, 97, 98, 99]])

x + y
array([[  0,   1,   2, ...,  97,  98,  99],
       [  1,   2,   3, ...,  98,  99, 100],
       [  2,   3,   4, ...,  99, 100, 101],
       ...,
       [ 97,  98,  99, ..., 194, 195, 196],
       [ 98,  99, 100, ..., 195, 196, 197],
       [ 99, 100, 101, ..., 196, 197, 198]])
       
x.shape
(100, 1)

y.shape
(1, 100)

grid = x + y

plt.figure(figsize=(6,6))
plt.imshow(grid)

x, y = np.ogrid[0:50,0:100]
grid = x + y 

plt.figure(figsize=(6,6))
plt.imshow(grid)

h,w = circle(10,10,10) # 원점 (10,10)을 중심으로 반지름이 10인 원 그리기 
grid = x + y 
grid[h,w] = 255

grid.shape
# (100, 100)

plt.figure(figsize=(6,6))
plt.imshow(grid) # ogrid로 만든 좌표축에 circle을 포함시켰다

xx, yy = np.ogrid[0:10:3j,0:10:3j] # 0부터 10까지 숫자를 3등분 해서 좌표축을 만든다  
grid = xx + yy 
h,w = circle(1,1,1)
grid[h,w] = 10

xx
# array([[ 0.],
       [ 5.],
       [10.]])
       
yy
# array([[ 0.,  5., 10.]])

grid.shape
# (3, 3)

plt.figure(figsize=(6,6))
plt.imshow(grid)

 

grid[...] = 0

plt.imshow(grid)

x, y = np.ogrid[0:10, 0:10]

grid = x + y 

grid[2*x-y==0]
# array([ 0,  3,  6,  9, 12])

grid # y = 2x 
array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9],
       [ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10],
       [ 2,  3,  4,  5,  6,  7,  8,  9, 10, 11],
       [ 3,  4,  5,  6,  7,  8,  9, 10, 11, 12],
       [ 4,  5,  6,  7,  8,  9, 10, 11, 12, 13],
       [ 5,  6,  7,  8,  9, 10, 11, 12, 13, 14],
       [ 6,  7,  8,  9, 10, 11, 12, 13, 14, 15],
       [ 7,  8,  9, 10, 11, 12, 13, 14, 15, 16],
       [ 8,  9, 10, 11, 12, 13, 14, 15, 16, 17],
       [ 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]])
x, y = np.ogrid[-10:10, -10:10]

grid = x + y 

grid[...] = 0
grid[x+2*y-3 == 0] # x + 2y -3 = 0 값을 0으로 잡아 놨기 때문에 그래프를 그릴 수 없다 
# array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0])

# 값을 0으로 잡지 않고 했을 때는 그래프를 그릴 수 있다 
grid = x + y 
grid[x+2*y-3 == 0]
# array([-3, -2, -1,  0,  1,  2,  3,  4,  5,  6])

x, y = np.ogrid[-5:5, -5:5]
grid = x + y 

grid 
array([[-10,  -9,  -8,  -7,  -6,  -5,  -4,  -3,  -2,  -1],
       [ -9,  -8,  -7,  -6,  -5,  -4,  -3,  -2,  -1,   0],
       [ -8,  -7,  -6,  -5,  -4,  -3,  -2,  -1,   0,   1],
       [ -7,  -6,  -5,  -4,  -3,  -2,  -1,   0,   1,   2],
       [ -6,  -5,  -4,  -3,  -2,  -1,   0,   1,   2,   3],
       [ -5,  -4,  -3,  -2,  -1,   0,   1,   2,   3,   4],
       [ -4,  -3,  -2,  -1,   0,   1,   2,   3,   4,   5],
       [ -3,  -2,  -1,   0,   1,   2,   3,   4,   5,   6],
       [ -2,  -1,   0,   1,   2,   3,   4,   5,   6,   7],
       [ -1,   0,   1,   2,   3,   4,   5,   6,   7,   8]])
       
grid[x==y] # 조건식을 둘수 있다 
# array([-10,  -8,  -6,  -4,  -2,   0,   2,   4,   6,   8])

x == y 
# array([[ True, False, False, False, False, False, False, False, False, False],
       [False,  True, False, False, False, False, False, False, False, False],
       [False, False,  True, False, False, False, False, False, False, False],
       [False, False, False,  True, False, False, False, False, False, False],
       [False, False, False, False,  True, False, False, False, False, False],
       [False, False, False, False, False,  True, False, False, False, False],
       [False, False, False, False, False, False,  True, False, False, False],
       [False, False, False, False, False, False, False,  True, False, False],
       [False, False, False, False, False, False, False, False,  True, False],
       [False, False, False, False, False, False, False, False, False, True]])
       
grid[x==y] = 0 
grid 
array([[ 0, -9, -8, -7, -6, -5, -4, -3, -2, -1],
       [-9,  0, -7, -6, -5, -4, -3, -2, -1,  0],
       [-8, -7,  0, -5, -4, -3, -2, -1,  0,  1],
       [-7, -6, -5,  0, -3, -2, -1,  0,  1,  2],
       [-6, -5, -4, -3,  0, -1,  0,  1,  2,  3],
       [-5, -4, -3, -2, -1,  0,  1,  2,  3,  4],
       [-4, -3, -2, -1,  0,  1,  0,  3,  4,  5],
       [-3, -2, -1,  0,  1,  2,  3,  0,  5,  6],
       [-2, -1,  0,  1,  2,  3,  4,  5,  0,  7],
       [-1,  0,  1,  2,  3,  4,  5,  6,  7,  0]])
       
grid[x==y] = 255
grid # y = x / 통념상 y = x일 때 왼쪽 아래에서 오른쪽 위로 뻗는 직선이 맞지만 grid함수로 만들어진 좌표축은 y축 방향이 ↓(아래쪽 화살표) 아래로 향하기 때문에 왼쪽 위에서 오른쪽 아래로 뻗는 직선이 나온다   
array([[255,  -9,  -8,  -7,  -6,  -5,  -4,  -3,  -2,  -1],
       [ -9, 255,  -7,  -6,  -5,  -4,  -3,  -2,  -1,   0],
       [ -8,  -7, 255,  -5,  -4,  -3,  -2,  -1,   0,   1],
       [ -7,  -6,  -5, 255,  -3,  -2,  -1,   0,   1,   2],
       [ -6,  -5,  -4,  -3, 255,  -1,   0,   1,   2,   3],
       [ -5,  -4,  -3,  -2,  -1, 255,   1,   2,   3,   4],
       [ -4,  -3,  -2,  -1,   0,   1, 255,   3,   4,   5],
       [ -3,  -2,  -1,   0,   1,   2,   3, 255,   5,   6],
       [ -2,  -1,   0,   1,   2,   3,   4,   5, 255,   7],
       [ -1,   0,   1,   2,   3,   4,   5,   6,   7, 255]])

mgrid

 

len(np.mgrid[0:100,0:100])
# 2

a, b = np.mgrid[0:100,0:100]
aa, bb = np.mgrid[0:10:5j,0:10:5j] # 0부터 10까지 숫자를 5등분 해서 좌표축을 만든다 
a
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
       17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
       34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
       51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67,
       68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84,
       85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99])
       

aa
array([[ 0. ,  0. ,  0. ,  0. ,  0. ],
       [ 2.5,  2.5,  2.5,  2.5,  2.5],
       [ 5. ,  5. ,  5. ,  5. ,  5. ],
       [ 7.5,  7.5,  7.5,  7.5,  7.5],
       [10. , 10. , 10. , 10. , 10. ]])
       
b
array([[ 0,  1,  2, ..., 97, 98, 99],
       [ 0,  1,  2, ..., 97, 98, 99],
       [ 0,  1,  2, ..., 97, 98, 99],
       ...,
       [ 0,  1,  2, ..., 97, 98, 99],
       [ 0,  1,  2, ..., 97, 98, 99],
       [ 0,  1,  2, ..., 97, 98, 99]])
       
a.shape
(100, 100)

b.shape
(100, 100)

grid = a + b

plt.figure(figsize=(6,6))
plt.imshow(grid)

c, d = np.mgrid[0:10,0:10]
c + d # 안에 있는 값은 의미 없다 / 좌표축을 만들어 준다는 것이 중요하다 
array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9],
       [ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10],
       [ 2,  3,  4,  5,  6,  7,  8,  9, 10, 11],
       [ 3,  4,  5,  6,  7,  8,  9, 10, 11, 12],
       [ 4,  5,  6,  7,  8,  9, 10, 11, 12, 13],
       [ 5,  6,  7,  8,  9, 10, 11, 12, 13, 14],
       [ 6,  7,  8,  9, 10, 11, 12, 13, 14, 15],
       [ 7,  8,  9, 10, 11, 12, 13, 14, 15, 16],
       [ 8,  9, 10, 11, 12, 13, 14, 15, 16, 17],
       [ 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]])
       

(c + d)[(0,1),(0,1)] = 0 # 흑백 이미지 / 2차원 좌표축 만드는 것

meshgrid vs ogrid vs mgrid

mesgrid ogrid mgrid
indexer 표현 X indexer 표현 O indexer 표현 O
행과 열이 바뀌어 표현한다 중복된것 줄여서작게 표현한다 다 표현해 준다
j(n등분)를 사용 X j(n등분)를 사용 O j(n등분)를 사용 O
비어 있는 2차원 좌표축을 그릴때 사용한다
me1, me2 = np.meshgrid(np.arange(100),np.arange(100))
og1, og2 = np.ogrid[0:100,0:100]
mg1, mg2 = np.mgrid[0:100,0:100]

np.array_equal(mg1+mg2, og1+og2) # mg1+mg2(mgrid)는 모든 수 다 표현해 준다 / or1+or2(ogrid)는 broadcasting 연산을 하도록 표현한다 
# True

mg1+mg2
array([[  0,   1,   2, ...,  97,  98,  99],
       [  1,   2,   3, ...,  98,  99, 100],
       [  2,   3,   4, ...,  99, 100, 101],
       ...,
       [ 97,  98,  99, ..., 194, 195, 196],
       [ 98,  99, 100, ..., 195, 196, 197],
       [ 99, 100, 101, ..., 196, 197, 198]])
       
og1+og2
array([[  0,   1,   2, ...,  97,  98,  99],
       [  1,   2,   3, ...,  98,  99, 100],
       [  2,   3,   4, ...,  99, 100, 101],
       ...,
       [ 97,  98,  99, ..., 194, 195, 196],
       [ 98,  99, 100, ..., 195, 196, 197],
       [ 99, 100, 101, ..., 196, 197, 198]])

mg1 == og1
array([[ True,  True,  True, ...,  True,  True,  True],
       [ True,  True,  True, ...,  True,  True,  True],
       [ True,  True,  True, ...,  True,  True,  True],
       ...,
       [ True,  True,  True, ...,  True,  True,  True],
       [ True,  True,  True, ...,  True,  True,  True],
       [ True,  True,  True, ...,  True,  True,  True]])

mg2 == og2
array([[ True,  True,  True, ...,  True,  True,  True],
       [ True,  True,  True, ...,  True,  True,  True],
       [ True,  True,  True, ...,  True,  True,  True],
       ...,
       [ True,  True,  True, ...,  True,  True,  True],
       [ True,  True,  True, ...,  True,  True,  True],
       [ True,  True,  True, ...,  True,  True,  True]])

np.array_equal(mg1+mg2, me1+me2) 
# True

mg1 == me1 # 중앙 대각선만 일치한다 
array([[ True, False, False, ..., False, False, False],
       [False,  True, False, ..., False, False, False],
       [False, False,  True, ..., False, False, False],
       ...,
       [False, False, False, ...,  True, False, False],
       [False, False, False, ..., False,  True, False],
       [False, False, False, ..., False, False,  True]])

mg1 == me1.T # 둘 중 하나를 전치행렬(Transposed matrix)을 구하면 둘이 같아 진다 
array([[ True,  True,  True, ...,  True,  True,  True],
       [ True,  True,  True, ...,  True,  True,  True],
       [ True,  True,  True, ...,  True,  True,  True],
       ...,
       [ True,  True,  True, ...,  True,  True,  True],
       [ True,  True,  True, ...,  True,  True,  True],
       [ True,  True,  True, ...,  True,  True,  True]])

mg2 == me2.T
array([[ True,  True,  True, ...,  True,  True,  True],
       [ True,  True,  True, ...,  True,  True,  True],
       [ True,  True,  True, ...,  True,  True,  True],
       ...,
       [ True,  True,  True, ...,  True,  True,  True],
       [ True,  True,  True, ...,  True,  True,  True],
       [ True,  True,  True, ...,  True,  True,  True]])

np.array_equal(og1+og2, me1+me2) 
# True

og1+og2 
array([[  0,   1,   2, ...,  97,  98,  99],
       [  1,   2,   3, ...,  98,  99, 100],
       [  2,   3,   4, ...,  99, 100, 101],
       ...,
       [ 97,  98,  99, ..., 194, 195, 196],
       [ 98,  99, 100, ..., 195, 196, 197],
       [ 99, 100, 101, ..., 196, 197, 198]])

me1 + me2
array([[  0,   1,   2, ...,  97,  98,  99],
       [  1,   2,   3, ...,  98,  99, 100],
       [  2,   3,   4, ...,  99, 100, 101],
       ...,
       [ 97,  98,  99, ..., 194, 195, 196],
       [ 98,  99, 100, ..., 195, 196, 197],
       [ 99, 100, 101, ..., 196, 197, 198]])

og1 == me1.T
array([[ True,  True,  True, ...,  True,  True,  True],
       [ True,  True,  True, ...,  True,  True,  True],
       [ True,  True,  True, ...,  True,  True,  True],
       ...,
       [ True,  True,  True, ...,  True,  True,  True],
       [ True,  True,  True, ...,  True,  True,  True],
       [ True,  True,  True, ...,  True,  True,  True]])

og2 == me2.T
array([[ True,  True,  True, ...,  True,  True,  True],
       [ True,  True,  True, ...,  True,  True,  True],
       [ True,  True,  True, ...,  True,  True,  True],
       ...,
       [ True,  True,  True, ...,  True,  True,  True],
       [ True,  True,  True, ...,  True,  True,  True],
       [ True,  True,  True, ...,  True,  True,  True]])

grid[c==d] # boolena indexing 방식 
# array([-10,  -8,  -6,  -4,  -2,   0,   2,   4,   6,   8])

c==d
array([[ True, False, False, False, False, False, False, False, False, False],
       [False,  True, False, False, False, False, False, False, False, False],
       [False, False,  True, False, False, False, False, False, False, False],
       [False, False, False,  True, False, False, False, False, False, False],
       [False, False, False, False,  True, False, False, False, False, False],
       [False, False, False, False, False,  True, False, False, False, False],
       [False, False, False, False, False, False,  True, False, False, False],
       [False, False, False, False, False, False, False,  True, False, False],
       [False, False, False, False, False, False, False, False,  True, False],
       [False, False, False, False, False, False, False, False, False, True]])

grid[2*c==d] = 255
grid 
array([[-10,  -9,  -8,  -7,  -6,  -5,  -4,  -3,  -2,  -1],
       [ -9,  -8,  -7,  -6,  -5,  -4,  -3,  -2,  -1,   0],
       [ -8,  -7,  -6,  -5,  -4,  -3,  -2,  -1,   0,   1],
       [ -7, 255,  -5,  -4,  -3,  -2,  -1,   0,   1,   2],
       [ -6,  -5,  -4, 255,  -2,  -1,   0,   1,   2,   3],
       [ -5,  -4,  -3,  -2,  -1, 255,   1,   2,   3,   4],
       [ -4,  -3,  -2,  -1,   0,   1,   2, 255,   4,   5],
       [ -3,  -2,  -1,   0,   1,   2,   3,   4,   5, 255],
       [ -2,  -1,   0,   1,   2,   3,   4,   5,   6,   7],
       [ -1,   0,   1,   2,   3,   4,   5,   6,   7,   8]])

circle

x, y = circle(3,3,3) # 원점이 3,3이고 반지름이 3인 원 
x
# array([[-5],
       [-4],
       [-3],
       [-2],
       [-1],
       [ 0],
       [ 1],
       [ 2],
       [ 3],
       [ 4]])
       
y
# array([[-5, -4, -3, -2, -1,  0,  1,  2,  3,  4]])

Scikit learn에서는 font를 지원하지 않는다

Scikit Masking

im1 = np.zeros((200,200))
im2 = np.zeros((200,200))

h1,w1 = rectangle((40,40),(160,160))
h2,w2 = circle(100,100,50)

im1[h1,w1] = 255
im2[h2,w2] = 255
plt.imshow(im1, cmap='gray');

plt.imshow(im2, cmap='gray');

OpenCV

import numpy as np
import cv2 # opencv 

im = cv2.imread('people.jpg')

type(im) # opencv로 불러올 때도 im객체가 ndarray이기 때문에 mutable이다 
# numpy.ndarray

# im객체가 rectangel이라는 함수를 사용하면 결과가 누적된다 (mutable)
im_r = cv2.rectangle(im, (100,100), (250,250), (255,0,0), 10) # 두번째 인자는 직사각형에서 왼쪽 위 좌표, 오른쪽 아래 좌표 / 세번째 인자는 RGB / 네번째 인자는 테두리 크기
plt.figure(figsize=(6,6))
plt.imshow(im_r);

텍스트 그리기

opencv에서는 font변경이 한정적이기 때문에 기본적으로 한글 사용 불가하다

opencv를 font를 바꾸고 나서 compile하면 한글 사용할 수 있다

beatmap형태를 지원한다

im = cv2.imread('people.jpg')
cv2.putText(im, 'moon', (200,100), 1, 6, (255,0,0), 2)  # 이미지 / 텍스트 / 위치 / 폰트 / 폰트크기 / RGB / 두께  

for i in dir(cv2):
  if 'FONT' in i:
    print(i, getattr(cv2, i))
plt.figure(figsize=(6,6))
plt.imshow(im)
im = cv2.imread('people.jpg')
cv2.putText(im, '한글', (200,100), 1, 6, (255,0,0), 2)
plt.figure(figsize=(6,6))
plt.imshow(im)

이미지 합성

주의 해야할 연산 가지

1. saturated : 255넘을 나머지 무시 => cv2.add

2. modular : % 255넘을 255나눈 나머지 => np.add

im1 = cv2.imread('people.jpg') 
im2 = cv2.imread('apple.jpg')

cv2.addWeighted(im1, 0.7, im2, 0.3, 0) # im1의 불투명도를 70% im2의 불투명도를 30% / 두 개 이미지 shape이 같아야 한다 
# error: OpenCV(4.1.2) /io/opencv/modules/core/src/arithm.cpp:663: error: (-209:Sizes of input arguments do not match) The operation is neither 'array op array' (where arrays have the same size and the same number of channels), nor 'array op scalar', nor 'scalar op array' in function 'arithm_op'
im1.shape, im2.shape
((540, 540, 3), (1000, 816, 3))

im2[:540,:540].shape
im2 = im2[250:790,150:690]
blend = cv2.addWeighted(im1, 0.7, im2, 0.3, 0) # 합성 이미지 / blending => 크기와 채널이 같아야 한다 / 두 이미지를 합 할때 가중치를 두고 연산한다 (가중합) 

plt.imshow(blend)

Bitwise 연산

a = 20
bin(a)
# '0b10100'

a.bit_length()
# 5

a = 20
b = 21

# 10100 
# 10101
#   ↓ 
# 10100 
a&b # bitwise and 연산 / 둘 다 1일 때 1, 둘 중 하나라도 0이면 0 
# 20

# 10100 
# 10101
#   ↓ 
# 10101 
a|b # bitwise or 연산 / 둘 중 하나라도 1이면 1, 둘 다 0이면 0
# 21

bin(a),bin(b)
# ('0b10100', '0b10101')

a >> 2  # 두 칸 뒤로 / 10100 => 00101
# 5

a << 2 # 두 칸 앞으로 / 10100 => 1010000
# 80

이미지에서 bitwise 연산

im1 = cv2.imread('people.jpg', 0)
im2 = cv2.imread('apple.jpg', 0)

im2 = im2[250:790,150:690]
t=cv2.bitwise_and(im1, im2)

plt.imshow(t, cmap='gray') # masking

Opencv masking

im1 = np.zeros((200,200))
im2 = np.zeros((200,200))

h1,w1 = rectangle((40,40),(160,160))
h2,w2 = circle(100,100,50)

im1[h1,w1] = 255
im2[h2,w2] = 255
plt.imshow(cv2.bitwise_and(im1,im2), cmap='gray');

plt.imshow(cv2.bitwise_or(im1,im2), cmap='gray');

plt.imshow(cv2.bitwise_xor(im1,im2), cmap='gray');

PIL

from PIL import Image, ImageDraw, ImageDraw2, ImageOps
import PIL

im_pil = Image.open('people.jpg')

type(im_pil)
# PIL.JpegImagePlugin.JpegImageFile

dir(PIL.JpegImagePlugin.JpegImageFile)
PIL.JpegImagePlugin.JpegImageFile.__class__
# type

im_pil

 

type(ImageDraw.ImageDraw) # Class 
# type

type(ImageDraw.Draw) # function
# function

draw = ImageDraw.ImageDraw(im_pil) # composition 방식 
draw2 = ImageDraw.Draw(im_pil) # function 방식 / function 방식이지만 return이 instacne이기 때문에 헷갈려서 대문자로 만들었다   

draw.rectangle(((0,0),(100,100))) # mutable 방식 
draw2.rectangle(((100,100),(200,200)))

im_pil

type(ImageOps)
# module

ImageOps.expand(im_pil, 20) # composition 함수 / im_pil이라는 인스턴스를 인자로 넣고 사용하기 때문에 composition 방식이다 / 결과가

텍스트 그리기

im_pil = Image.open('people.jpg')
from PIL import ImageFont
draw = ImageDraw.ImageDraw(im_pil)
draw2 = ImageDraw.Draw(im_pil)
draw.text((120,100),'Chim') # mutable 방식 
im_pil

draw.text((100,120),'한글') # 기본적인 방식에서는 한글 지원 안한다 

UnicodeEncodeError: 'latin-1' codec can't encode characters in position 0-1: ordinal not in range(256)
!sudo apt-get install -y fonts-nanum # colab에서 사용시 폰트를 다운 받아야 한다 
!sudo fc-cache -fv
!rm ~/.cache/matplotlib -rf
font = ImageFont.truetype('NanumBarunGothic', 35) # PIL은 truetype으로 깔끔하게 나온다 
draw.text((240,170),'주호민', font=font, fill='pink') 
im_pil

 

 

반응형
728x90
반응형

Image 처리

import tensorflow as tf 
im = tf.io.read_file('people.jpg')
im = tf.image.decode_jpeg(im)
im
# <tf.Tensor: shape=(540, 540, 3), dtype=uint8, numpy=
# array([[[200, 188, 176],
#         [202, 190, 178],
#         [204, 192, 180],
#         ...,

상황에 따라 알맞은 라이브러리를 사용하면 된다

from scipy import ndimage # 기본적인 이미지 처리 할 수 있다 
from scipy import io
import imageio # image를 읽고 저장하는 기능만 제공한다 
import numpy as np
import scipy # low level이기 때문에 전문가들이 주로 사용한다 / image processing에 초첨이 맞추어져 있다 
# scipy-toolkit => scipy에서 파생되어 사용자 친화적인 high level인 라이브러리가 생겼다 
# scipy-toolkit image => scikit-image 
# scipy-toolkit machine learning => scikit-learn 
# opencv => Practical 
import cv2 # computer vision에 초점이 맞추어 져있다 / image array 구조가 Numpy와 다르다 
import matplotlib.pyplot as plt
from PIL import Image  # pythonic한 image 처리 라이브러리 
from skimage import io
import PIL

# !pip install -U opencv-python # opecnv 설치 / colab에서 하시면 설치하지 않으셔도 됩니다 / 파이썬 기반의 opencv는 한글처리 기능이 지원되지 않는다 
!pip list
Package                       Version
----------------------------- --------------
absl-py                       0.12.0
alabaster                     0.7.12
albumentations                0.1.12
altair                        4.1.0
appdirs                       1.4.4
argcomplete                   1.12.3
argon2-cffi                   21.1.0
arviz                         0.11.2
astor                         0.8.1
astropy                       4.3.1
astunparse                    1.6.3
atari-py                      0.2.9
atomicwrites                  1.4.0
attrs                         21.2.0
audioread                     2.1.9
autograd                      1.3
Babel                         2.9.1
backcall                      0.2.0
beautifulsoup4                4.6.3
bleach                        4.0.0
blis                          0.4.1
bokeh                         2.3.3
Bottleneck                    1.3.2
branca                        0.4.2
bs4                           0.0.1
...
dir(ndimage)
dir(io)
dir(imageio)
['RETURN_BYTES',
 '__builtins__',
 '__cached__',
 '__doc__',
 '__file__',
 '__loader__',
 '__name__',
 '__package__',
 '__path__',
 '__spec__',
 '__version__',
 'core',
 'formats',
 'get_reader',
 'get_writer',
 'help',
 'imread',
 'imsave',
 'imwrite',
 'mimread',
 'mimsave',
 'mimwrite',
 'mvolread',
 'mvolsave',
 'mvolwrite',
 'plugins',
 'read',
 'save',
 'show_formats',
 'volread',
 'volsave',
 'volwrite']
imageio.formats # 95가지 image format을 지원한다 
# <imageio.FormatManager with 96 registered formats>
imageio.imread('people.jpg')
# Array([[[201, 189, 177],
#         [203, 191, 179],
#         [204, 192, 180],
#         ...,
type(imageio.imread('people.jpg'))
# imageio.core.util.Array

issubclass(type(imageio.imread('people.jpg')), np.ndarray)
# True

imageio.core.util.Array.__base__ # 부모 클래스 
# numpy.ndarray

imageio.core.util.Array.mro() 
# [imageio.core.util.Array, numpy.ndarray, object]

OpenCV

opencv c버전은 c/c++ 관례를 따른다

dir(cv2)
['',
 'ACCESS_FAST',
 'ACCESS_MASK',
 'ACCESS_READ',
 'ACCESS_RW',
 'ACCESS_WRITE',
 'ADAPTIVE_THRESH_GAUSSIAN_C',
 'ADAPTIVE_THRESH_MEAN_C',
im2 = cv2.imread('people.jpg')
plt.imshow(im2)

RGB로 처리하는 것이 선명하게 잘 보여주지만 비용이 많이 들기 때문에

저가 모니터는 BGR 처리를 한다

opencv는 BGR 배열로 표현해준다

plt.imshow(im2[...,::-1])

im3 = cv2.imread('people.jgp') # 확장자를 잘 못 적어도 에러가 나지 않는다 
plt.imshow(im)

im1 = cv2.imread('people.jpg')    # 3차원으로 불러온다 / 컬러 이미지 

im2 = cv2.imread('people.jpg', 0) # 2차원으로 불러온다 / 흑백 이미지 

im1.shape, im2.shape
# ((540, 540, 3), (540, 540))

im3 = cv2.imread('people.jpg', 100)
im3.shape
# (135, 135, 3)
dir(cv2) # 대문자는 옵션이다 
for i in dir(cv2):
  if 'imread' in i:
    print(i)
# imread
# imreadmulti
for i in dir(cv2):
  if 'IMREAD' in i:
    print(i, getattr(cv2, i))   # 옵션 
    
IMREAD_ANYCOLOR 4
IMREAD_ANYDEPTH 2
IMREAD_COLOR 1
IMREAD_GRAYSCALE 0
IMREAD_IGNORE_ORIENTATION 128
IMREAD_LOAD_GDAL 8
IMREAD_REDUCED_COLOR_2 17
IMREAD_REDUCED_COLOR_4 33
IMREAD_REDUCED_COLOR_8 65
IMREAD_REDUCED_GRAYSCALE_2 16
IMREAD_REDUCED_GRAYSCALE_4 32
IMREAD_REDUCED_GRAYSCALE_8 64
IMREAD_UNCHANGED -1

Saturated / Modular 연산

im = cv2.imread('people.jpg')
im 
# array([[[177, 189, 201],
#         [179, 191, 203],
#         [180, 192, 204],
#         ...,
cv2.add(im, im) # 가장 큰 값이 255이기 때문에 합이 255를 넘어가면 saturated 연산이 적용된다
array([[[255, 255, 255],
        [255, 255, 255],
        [255, 255, 255],
        ...,
        [252, 255, 255],
        [254, 255, 255],
        [255, 255, 255]],
(177+177) - 256 # im의 첫번쨰 값: 177 / 177 + 177이 255을 초과 했기 때문에 modular연산에서는 256(dtype이 8이었기 때문에 2^8)을 빼는 연산을 한다 
# 98
im.dtype
# dtype('uint8')

np.add(im, im) # modular 연산 
# array([[[ 98, 122, 146],
#         [102, 126, 150],
#         [104, 128, 152],
#         ...,

Basic Operation

im[100,40]
# array([183, 198, 207], dtype=uint8)

im[100,40,2]
# 207

im.item(100,40,2)
# 207

im[100,40,2] = 100

im.item(100,40,2)
# 100

im.itemset((100,40,2), 207) # 값 조정 
im.item(100,40,2)
# 207

im[200:30,330:2]

array([], shape=(0, 0, 3), dtype=uint8)
plt.imshow(im[...,::-1])

Channel

im_cv = cv2.imread('people.jpg')
im[...,2]
array([[201, 203, 204, ..., 193, 194, 196],
       [200, 202, 203, ..., 201, 203, 204],
       [199, 201, 202, ..., 209, 210, 211],
       ...,
       [114, 112, 120, ...,  98, 109, 114],
       [116, 112, 120, ...,  96, 103, 106],
       [111, 107, 115, ..., 102, 103, 103]], dtype=uint8)
len(cv2.split(im))
# 3
plt.imshow(im_cv)

B, G, R = cv2.split(im_cv) # function 방식 
im_cv = cv2.merge((R,G,B))
plt.imshow(im_cv)

for i in dir(cv2):
  if 'COLOR_BGR2' in i:
    print(i, getattr(cv2, i))
    
COLOR_BGR2BGR555 22
COLOR_BGR2BGR565 12
COLOR_BGR2BGRA 0
COLOR_BGR2GRAY 6
COLOR_BGR2HLS 52
COLOR_BGR2HLS_FULL 68
COLOR_BGR2HSV 40
COLOR_BGR2HSV_FULL 66
COLOR_BGR2LAB 44
COLOR_BGR2LUV 50
COLOR_BGR2Lab 44
COLOR_BGR2Luv 50
COLOR_BGR2RGB 4
COLOR_BGR2RGBA 2
COLOR_BGR2XYZ 32
COLOR_BGR2YCR_CB 36
COLOR_BGR2YCrCb 36
COLOR_BGR2YUV 82
COLOR_BGR2YUV_I420 128
COLOR_BGR2YUV_IYUV 128
COLOR_BGR2YUV_YV12 132
cv2.cvtColor(im_cv, cv2.COLOR_BGR2GRAY) # 색공간 바꾸기 
array([[187, 189, 190, ..., 152, 153, 155],
       [186, 188, 189, ..., 160, 162, 163],
       [185, 187, 188, ..., 170, 171, 172],
       ...,
       [100,  96, 103, ..., 119, 130, 135],
       [100,  96, 103, ..., 119, 126, 129],
       [ 95,  91,  98, ..., 125, 126, 126]], dtype=uint8)
cv2.cvtColor(im_cv, cv2.COLOR_BGR2RGB)
array([[[177, 189, 201],
        [179, 191, 203],
        [180, 192, 204],
        ...,
for i in dir(cv2):
  if 'BORDER_' in i :
    print(i, getattr(cv2, i))
BORDER_CONSTANT 0
BORDER_DEFAULT 4
BORDER_ISOLATED 16
BORDER_REFLECT 2
BORDER_REFLECT101 4
BORDER_REFLECT_101 4
BORDER_REPLICATE 1
BORDER_TRANSPARENT 5
BORDER_WRAP 3
plt.imshow(cv2.copyMakeBorder(im_cv, 10,10,10,10,0))

plt.imshow(np.pad(im_cv,[(10,10),(10,10),(0,0)], mode='constant'))

PIL

PIL은 이미지 파일 자체에 대한 내용도 알 수 있다

meta data에 대한 정보가 필요할떄 유용하다

im4 = Image.open('people.jpg') # mutable 방식 지원 
plt.imshow(im4)

b = np.array(im4)
Image.fromarray(b)

 

type(b)
# numpy.ndarray

type(im4) # 데이터 구조 자체가 numpy와 다르다 하지만 Numpy와 호환이 된다 
# PIL.JpegImagePlugin.JpegImageFile

im4.format_description
# 'JPEG (ISO 10918)'

im4.getexif().keys()
# KeysView(<PIL.Image.Exif object at 0x7f424eb895d0>)
im_pil = Image.open('people.jpg')
R, G, B = im_pil.split() # method 방식 
Image.merge('RGB', (R,G,B))

Image.merge('RGB', (B,G,R))

'ImageMode' in dir(PIL) # composition 방식으로 만들어짐 
# True
from PIL import ImageMode
ImageMode._modes # Image.merge(mode, bands) 모드 자리에 들어 갈 수 있는 키값들 
{'1': <PIL.ImageMode.ModeDescriptor at 0x7f4249d1b350>,
 'CMYK': <PIL.ImageMode.ModeDescriptor at 0x7f4249d45bd0>,
 'F': <PIL.ImageMode.ModeDescriptor at 0x7f4249d45d50>,
 'HSV': <PIL.ImageMode.ModeDescriptor at 0x7f4249d45590>,
 'I': <PIL.ImageMode.ModeDescriptor at 0x7f4249d45d90>,
 'I;16': <PIL.ImageMode.ModeDescriptor at 0x7f4249d45a50>,
 'I;16B': <PIL.ImageMode.ModeDescriptor at 0x7f4249d45f50>,
 'I;16BS': <PIL.ImageMode.ModeDescriptor at 0x7f4249d45e90>,
 'I;16L': <PIL.ImageMode.ModeDescriptor at 0x7f4249d45410>,
 'I;16LS': <PIL.ImageMode.ModeDescriptor at 0x7f4249d450d0>,
 'I;16N': <PIL.ImageMode.ModeDescriptor at 0x7f4249d45390>,
 'I;16NS': <PIL.ImageMode.ModeDescriptor at 0x7f4249d31610>,
 'I;16S': <PIL.ImageMode.ModeDescriptor at 0x7f4249d45ad0>,
 'L': <PIL.ImageMode.ModeDescriptor at 0x7f4249d1bd10>,
 'LA': <PIL.ImageMode.ModeDescriptor at 0x7f4249d45190>,
 'LAB': <PIL.ImageMode.ModeDescriptor at 0x7f4249d45110>,
 'La': <PIL.ImageMode.ModeDescriptor at 0x7f4249d457d0>,
 'P': <PIL.ImageMode.ModeDescriptor at 0x7f4249d45e50>,
 'PA': <PIL.ImageMode.ModeDescriptor at 0x7f4249d452d0>,
 'RGB': <PIL.ImageMode.ModeDescriptor at 0x7f4249d45850>,
 'RGBA': <PIL.ImageMode.ModeDescriptor at 0x7f4249d45210>,
 'RGBX': <PIL.ImageMode.ModeDescriptor at 0x7f4249d45e10>,
 'RGBa': <PIL.ImageMode.ModeDescriptor at 0x7f4249d45510>,
 'YCbCr': <PIL.ImageMode.ModeDescriptor at 0x7f4249d45a90>}
 
 im_pil.convert('L')

PIL은 pad를 지원하지 않는다

im_pil.width
# 540

im_pil.height
# 540

im_pil.info # meat data 
# {'jfif': 257, 'jfif_density': (1, 1), 'jfif_unit': 0, 'jfif_version': (1, 1)}

im_new = Image.new('RGB',(im_pil.width + 20, im_pil.height + 20))
im_new.paste(im_pil, (20,20)) # output이 없다 / mutable 방식 
im_new

from PIL import ImageOps
ImageOps.expand(im_pil, 10) # composition 방법

PIL을 사용하는 이유

tensorflow와 pytorch에서 이미지를 불러올 때 내부적으로 처리하기 때문에 알고 있어야 한다

tf.keras.preprocessing.image.load_img('people.jpg')

Scikit image

from skimage.color import rgb2gray # skimage는 BGR를 지원하지 안는다 
plt.imshow(rgb2gray(im[...,::-1]))

plt.imshow(rgb2gray(im[...,::-1]),  cmap='gray')

plt.imshow(np.mean(im,2))

R,G,B = im[...,0], im[...,1], im[...,2]
gray = R*0.13 + G*0.15 + B*0.14
np.mean(im,2) # (im[...,0]) + im[...,1] + im[...,2]) / 3
plt.imshow(np.mean(im,2), cmap='gray')

각각 이미지 처리 라이브러리에 대한 장점들

속도 차이 : opencv > numpy > PIL

im = tf.io.read_file('people.jpg')
im = tf.image.decode_jpeg(im)  

im = imageio.imread('people.jpg') # Array (array 상속) / 다양한 포맷 지원 / 여러 이미지 불러올 수 있다 

im = cv2.imread('people.jpg') # c/c++ style / array가 BGR로 구성되어 있다 / opencv는 gpu도 지원하기 때문에 속도가 가장 빠르다 

im = Image.open('people.jpg') # numpy와 호환이 되지만 numpy format은 아니다 / PIL이 가장 느리다 

plt.imread('people.jpg') # png파일 불러오지 못함 
array([[[201, 189, 177],
        [203, 191, 179],
        [204, 192, 180],
        ...,
        [194, 158, 126],
from skimage import io
im = io.imread('people.jpg')
plt.imshow(im)

im = Image.open('people.jpg')
im2 = im.getdata()

type(im2)
# ImagingCore

list(im2)
'__iter__' in dir(im2)
# False

im2.__class__.mro() # ImagingCore에 __iter__가 있기때문에 반복, 순회 가능
# [ImagingCore, object]

im = Image.open('people.jpg').convert('1') # meta data를 갖고 있기때문에 가능한 방법
im.getcolors()
# [(142786, 0), (148814, 255)]

 

복습

Stride

a = np.arange(24).reshape(6,4) # 내부적으로는 일렬로 저장된다 
a.flags # Information about the memory layout of the array.
#  C_CONTIGUOUS : True
#  F_CONTIGUOUS : False
#  OWNDATA : False
#  WRITEABLE : True
#  ALIGNED : True
#  WRITEBACKIFCOPY : False
#  UPDATEIFCOPY : False

a.dtype
# dtype('int64')

a.strides # 1차원의 array를 조립하는 가이드 라인 
# (32, 8)

a.itemsize # memory 크기가 8인 정육각형 
# 8

a 
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15],
       [16, 17, 18, 19],
       [20, 21, 22, 23]])
       
a.shape
# (6, 4)

a.sum(0) # axis = 0 기준으로 더한다 
# array([60, 66, 72, 78])

a.sum(1) # axis = 1 기준으로 더한다 
# array([ 6, 22, 38, 54, 70, 86])

b = np.arange(24).reshape(2,3,4)
b.shape
# (2, 3, 4)

b.sum(axis=1) # shape에서 index 1을 지우면 결과는 (2,4) 
array([[12, 15, 18, 21],
       [48, 51, 54, 57]])
       
b.sum(axis=2) # shape에서 index 2를 지우면 결과는 (2,3) 
array([[ 6, 22, 38],
       [54, 70, 86]])

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

반응형
728x90
반응형
import matplotlib
len(dir(matplotlib)) 
# 165

import matplotlib.pyplot as plt 
len(dir(matplotlib))  # colab에서는 matplotlib이나 matplotlib.pyplot이나 똑같이 나오네요 
# 165

!pip list # matplotlib version = 3.2.2
!python --version

Image

Image는 2차원의 함수로 정의 한다

Image는 공간에 어떤한 값을 넣었을 값에 대한 강도를 나타낸다

 

Color image는 3 이상 값의 조합으로 좌표에 대한 강도를 나타낸다

 

어떻게 강도를 표현하느냐에 따라 함수가 달라질 있다

특히 함수가 한정적일 경우 디지털 이미지라고 부른다

 

직사각형 형태의 어떤 값으로 강도를 나타낸다

정의 자체는 함수이지만 함수를 좌표계로 구성되어 있는 array로 표현할 있다

Image의 두 종류

1. 벡터 이미지

- 각각 좌표에 대한 함수가 아니라 이미지 자체를 함수로 표현

- 점과 점을 연결해 수학적 원리로 그림을 그려 표현하는 방식이다

- 이미지의 크기를 늘리고 줄여도 손상되지 않는다

- 사진과 같은 복잡한 그림을 표현하려면 컴퓨터에 엄청난 부담을 주기 때문에 웹에서는 사용되지 않는다

- 최근에는 컴퓨터 하드웨어의 발달로 웹사이트의 로고 아이콘 표시에 벡터 그래픽이 사용되고 있다

- ex) ai, eps, svg

 

2. 비트맵 이미지

- 서로 다른 (픽셀)들의 조합으로 그려지는 이미지 표현 방식이다

- 비트맵 이미지는 크기를 늘리거나 줄이면 원본 이미지에 손상되어 번져 보인다

- 컴퓨터에 부담을 주는 구조를 갖고 있기 때문에, 웹에서 이미지를 표시할 때는 주로 비트맵 이미지를 사용한다

- ex) jpg, jpeg, png, gif

색을 표현하는 두 가지 방식

1. HSV

- 밝기로 구분한다

- 색상(Hue), 채도(Saturation), 명도(Value) 좌표를 써서 특정한 색을 지정한다

2. RGB

- 세가지 색이 섞여서 색을 표현한다

- 3장의 array가 합쳐진 것이다

비트맵 이미지를 표현하는 단위

1. Pixel @@@ 추가 설명

- 픽셀은 상대적 크기로 표현한다

- 픽셀은 정사각형으로 표현한다

- ppi가 클수록 1인치당 픽셀의 밀도가 더 높고 더 선명하게 보인다

2. Voxel

- 3차원 이미지에서는 voxel이라고 한다

이미지 처리를 위한 사전단계

1. Image파일을 바이너리 형태로 불러온다

2. image를 array로 바꿔준다

 

with open('Elon.jpg', 'rb') as f: # 기본 옵션 r은 unicode형식으로 불러온다  / rb는 바이너리로 불러온다 
  print(f.read())                 # 파이썬에서는 이미지는 불러올수는 있지만 각각의 값이 어떤 색을 표현하는지 해석할 수 없다 

#  b'\xff\xd8\xff\xe1\x00\x18Exif\x00\x00II*\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xec\x00\x11Ducky\x00\x01\x00\x04...
import tensorflow as tf
im = tf.io.read_file('Elon.jpg') # vectorization으로 불러온다 / array연산을 최적화 하도록 불러온다 
im
# <tf.Tensor: shape=(), dtype=string, numpy=b'\xff\xd8\xff\xe1\x00\x18Exif\x00\x00II*\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0
# .numpy() 하면 기타정보 빼고 내용만 보여줌
im.numpy()

# b'\xff\xd8\xff\xe1\x00\x18Exif\x00\x00II*\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xec\x00\x11Ducky\x00\x01\x00\x04
t = tf.image.decode_gif(im) # 바이너리 값을 해석해준다 
t
# <tf.Tensor: shape=(1, 600, 960, 3), dtype=uint8, numpy=
# array([[[[ 4,  3,  9],
#          [ 3,  2, 10],
#          [ 4,  3, 11],
         ...,
t = tf.image.decode_image(im)
t
# <tf.Tensor: shape=(600, 960, 3), dtype=uint8, numpy=
# array([[[ 4,  3,  9],
#         [ 3,  2, 10],
#         [ 4,  3, 11],
import cv2
cv2.imread('Elon.jpg', ) # OpenCV는 BGR로 불러온다  / python으로 불러오는 것과 opencv로 불러오는 것은 편의성 옵션의 차이일 뿐이다 
# array([[[12,  4,  5],
#         [10,  2,  3],
#         [11,  2,  5],
s = cv2.imread('Elon.jpg', )
s.dtype # int이기 때문에 color에서 부호가 없다 / 2^8 = 256 => 0~255까지로 강도를 표현한다 
# dtype('uint8')

이미지를 불러올 때 tensor가 좋은 점은 gpu연산이 가능하기 때문에 대용량 이미지를 더 빠르게 불러올 수 있다

 

import matplotlib.pyplot as plt
plt.figure(figsize=(10,10))
plt.imshow(t) # array를 image로 해석하는 방법을 제공한다 / version 때문에 tensor를 직접 표현 못할 수 있다. 혹시 안된다면 colabd으로 사용해 보는 것을 추천한다
plt.colorbar(); # 컬러바 표시하기
plt.axis('off'); # 좌표 축 없애기

State machine

State Machine은 외부 입력에 따라 시스템의 상태가 결정되고,

이 상태와 입력에 의해서 시스템의 동작이 결정되는 시스템이다.

[특징]

1. 변수가 없다

2. figure안에 axes가 포함되어 있다. 그리고 plt.axes만 실행하면 plt.figure의 default값으로 자동실행 된다

3. 변수를 지정하지 않아도 상태를 보고 자동적으로 만들어 준다

 

# 같은 셀안에서 실행하면 
plt.figure();
plt.axes([0.5,0.5,1,1]); # figure 안에서 좌표형태로 보여준다 
plt.plot([4,5,4]);
plt.axes([0,0,0.5,0.5]);
plt.plot([4,5,4]);

plt.axes([0,0,0.5,0.5])

# plt.figure()
# plt.axes()
plt.plot([1,2,3]) # plot만 하면 figure, axes default값으로 자동 지정된다

 

s = t / 255
t.dtype
# tf.uint8

s.dtype
# tf.float32

plt.imshow(s) # 이미지를 구성하는 값들은 상대적인 개념이기 때문에 특정 값을 기준으로 나누어도 사람이 볼때는 똑같은 이미지로 볼 수 있다

im = tf.io.read_file('Elon.jpg')
t = tf.image.decode_image(im)
ss = tf.cast(t, 'float32')

ss.dtype
# tf.float32

sss = (ss/127.5) -1 # -1에서 1 사이로 변환해주는 방법 / 가장 큰 값이 255이기 때문에 절반으로 나누고 1을 빼면 -1과 1 사이의 값으로 구성된다 
import numpy as np
np.min(sss)
# -1.0

plt.imshow(sss) # imshow는 -1값은 표현하지 못하기 때문에 달라 보인다

plt.imshow(sss - 1)

sss.shape
# TensorShape([600, 960, 3])

sss[...,0][..., tf.newaxis]
# <tf.Tensor: shape=(600, 960, 1), dtype=float32, numpy=
# array([[[-0.96862745],
#         [-0.9764706 ],
#         [-0.96862745],
#         ...,
t[...,2] # color 이미지에서 채널 하나 빼면 흑백이 된다 / color는 각 픽셀당 3가지 값으로 표현해야 한다 
# <tf.Tensor: shape=(600, 960), dtype=uint8, numpy=
# array([[ 9, 10, 11, ...,  0,  0,  0],
#        [ 8, 10, 11, ...,  0,  0,  0],
#        [ 9, 10, 11, ...,  0,  0,  0],
#        ...,
#        [ 9, 11, 13, ..., 11, 11, 12],
#        [ 9, 11, 10, ..., 12, 12, 12],
#        [10, 11, 10, ..., 14, 14, 14]], dtype=uint8)>
plt.imshow(t[...,1]) # (0,1), (0,255) / 흑백 이미지는 기본 colormap으로 표현 / 색깔정보가 없으면 기본 색으로 입혀준다 /imshow는 default를 color로 간주한다

plt.imshow(t[...,1], cmap='gray') # 숫자 값이 크면 클 수록 밝게 표현한다

plt.imshow(t[...,1], cmap='gray_r') # 숫자 값이 크면 클 수록 어둡게 표현한다

plt.imshow(t[...,1], cmap=plt.cm.Reds)

plt.imshow(t[...,1], cmap=plt.cm.Reds_r)

import matplotlib
len(dir(matplotlib)) 
# 165

import matplotlib.pyplot as plt 
len(dir(matplotlib))  # colab에서는 matplotlib이나 matplotlib.pyplot이나 똑같이 나오네요 
# 165

!pip list # matplotlib version = 3.2.2
!python --version
# Python 3.7.11

Monkey patch

한국에서는 잠수함 패치라고 부르고 영어권에서는 게릴라 패치, 고릴라 패치, 몽키 패치라고 부른다 런타임상에서 함수, 메소드, 속성을 바꾸는 패치

 

from PIL import Image # 파이썬 스럽게 이미지를 처리해주는 라이브러리(일반인대상으로 가장 많이 사용한다)
Image.open('Elon.jpg')

im = Image.open('Elon.jpg')
dir(im) # numpy 방식이 아닌 numpy 호환 형태로 데이터를 갖고 있다 
['_Image__transformer',
 '__array_interface__',
 '__class__',
 '__copy__',
 '__delattr__',
 '__dict__',
 '__dir__',
 '__doc__',
 '__enter__',
 '__eq__',
 '__exit__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__getstate__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__le__',
 '__lt__',
 '__module__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__setstate__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__',
 '_close_exclusive_fp_after_loading',
 '_copy',
 '_crop',
 '_dump',
 '_ensure_mutable',
 '_exclusive_fp',
 '_exif',
 '_expand',
 '_get_safe_box',
 '_getexif',
 '_getmp',
 '_min_frame',
 '_new',
 '_open',
 '_repr_png_',
 '_seek_check',
 '_size',
 'alpha_composite',
 'app',
 'applist',
 'bits',
 'category',
 'close',
 'convert',
 'copy',
 'crop',
 'custom_mimetype',
 'decoderconfig',
 'decodermaxblock',
 'draft',
 'effect_spread',
 'entropy',
 'filename',
 'filter',
 'format',
 'format_description',
 'fp',
 'frombytes',
 'fromstring',
 'get_format_mimetype',
 'getbands',
 'getbbox',
 'getchannel',
 'getcolors',
 'getdata',
 'getexif',
 'getextrema',
 'getim',
 'getpalette',
 'getpixel',
 'getprojection',
 'height',
 'histogram',
 'huffman_ac',
 'huffman_dc',
 'icclist',
 'im',
 'info',
 'layer',
 'layers',
 'load',
 'load_djpeg',
 'load_end',
 'load_prepare',
 'load_read',
 'mode',
 'offset',
 'palette',
 'paste',
 'point',
 'putalpha',
 'putdata',
 'putpalette',
 'putpixel',
 'pyaccess',
 'quantization',
 'quantize',
 'readonly',
 'reduce',
 'remap_palette',
 'resize',
 'rotate',
 'save',
 'seek',
 'show',
 'size',
 'split',
 'tell',
 'thumbnail',
 'tile',
 'tobitmap',
 'tobytes',
 'toqimage',
 'toqpixmap',
 'tostring',
 'transform',
 'transpose',
 'verify',
 'width']
dir(im.getdata())
['__class__',
 '__delattr__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__getitem__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__le__',
 '__len__',
 '__lt__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 'bands',
 'box_blur',
 'chop_add',
 'chop_add_modulo',
 'chop_and',
 'chop_darker',
 'chop_difference',
 'chop_hard_light',
 'chop_invert',
 'chop_lighter',
 'chop_multiply',
 'chop_or',
 'chop_overlay',
 'chop_screen',
 'chop_soft_light',
 'chop_subtract',
 'chop_subtract_modulo',
 'chop_xor',
 'color_lut_3d',
 'convert',
 'convert2',
 'convert_matrix',
 'convert_transparent',
 'copy',
 'crop',
 'effect_spread',
 'entropy',
 'expand',
 'fillband',
 'filter',
 'gaussian_blur',
 'getband',
 'getbbox',
 'getcolors',
 'getextrema',
 'getpalette',
 'getpalettemode',
 'getpixel',
 'getprojection',
 'histogram',
 'id',
 'isblock',
 'mode',
 'modefilter',
 'new_block',
 'offset',
 'paste',
 'pixel_access',
 'point',
 'point_transform',
 'ptr',
 'putband',
 'putdata',
 'putpalette',
 'putpalettealpha',
 'putpalettealphas',
 'putpixel',
 'quantize',
 'rankfilter',
 'reduce',
 'resize',
 'save_ppm',
 'setmode',
 'size',
 'split',
 'transform2',
 'transpose',
 'unsafe_ptrs',
 'unsharp_mask']
im_ = np.array(im)
plt.imshow(im_)

 

tf.keras.preprocessing.image.load_img('Elon.jpg')

i  = tf.keras.preprocessing.image.load_img('Elon.jpg') # tensorflow, pytorch는 내부적으로 PIL 사용한다 
type(i)
# PIL.JpegImagePlugin.JpegImageFile

 

(X_train, y_train), (X_test, y_test) = tf.keras.datasets.mnist.load_data()
# Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz
# 11493376/11490434 [==============================] - 0s 0us/step
# 11501568/11490434 [==============================] - 0s 0us/step

plt.imshow(X_train[100])

np.set_printoptions(formatter={'int': '{:>01d}'.format}, linewidth=80) # int 데이터일 경우에 한 자리로 표현해 준다 
print(X_train[100])
[[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 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 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 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 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 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 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0 0 0 0 2 18 46 136 136 244 255 241 103 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0 0 15 94 163 253 253 253 253 238 218 204 35 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0 0 131 253 253 253 253 237 200 57 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0 155 246 253 247 108 65 45 0 0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0 207 253 253 230 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0 157 253 253 125 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0 89 253 250 57 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0 89 253 247 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 89 253 247 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 89 253 247 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 21 231 249 34 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 225 253 231 213 213 123 16 0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0 0 172 253 253 253 253 253 190 63 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0 0 2 116 72 124 209 253 253 141 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 25 219 253 206 3 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 104 246 253 5 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 213 253 5 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 26 226 253 5 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 132 253 209 3 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 78 253 86 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 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 0 0 0 0 0 0 0 0 0 0 0]]
plt.imshow(i)
plt.annotate('0', xy=(0,0))
# Text(0, 0, '0')

 

def visualize_bw(img):
  w, h = img.shape
  plt.figure(figsize=(10,10))
  plt.imshow(img, cmap='binary') # binary는 작은 숫자가 밝게 / gray는 큰 숫자가 밝게 
  for i in range(w):
    for j in range(h):
      x = img[j,i]
      plt.annotate(x, xy=(i,j), horizontalalignment='center', verticalalignment='center', color = 'black' if x <= 128 else 'white') # 점 하나당 대체 시킨다
      
visualize_bw(X_train[0])

Image processing

input image가 어떠한 함수에 의해 새로운 image가 output으로 나오는 것

array연산을 통해서 새로운 형태의 image를 만들어 내는 것

 

visualize_bw(X_train[5][::-1])
# 뒤집기

visualize_bw(X_train[5][4:24,4:24])
# 잘라내기

visualize_bw(np.flipud(X_train[5])) # flipud => 이미지 뒤집기

# 크기가 (20,20)인 흑백 이미지 생성
visualize_bw(np.ones((20,20)))

 

 

 

반응형
728x90
반응형

numpy

기계학습은 한번에 동시에 연산해야 하는데

array 연산

homogeneous / sequence : matrix

1차 : vector

2차 : matrix

3차이상 : tensor

 

섞이면

array는 자동으로 가장 큰 데이터 타입으로 바꿔주는데

tensor는 sequence를 mixed로 할수없다

torch도 sequence를 mixed로 할수없다

homogeneous는 데이터의 종류가 같다. 동질성

sequence는 indexing, slicing, sequence operator(vectorization)

 => 연산할 때 값이 아닌 인덱싱 이어준다

vectorization : loop없이 한번에 여러개의 연산을 elementwise 방식으로 동시에 하게 해준다. 

elementwise : 같은 위치의 값끼리, 자기에 맞는 위치의 값끼리 연산

 

numpy는 cpu vectorization 연산 지원(4코어면 4개 동시 연산), tensorflow는 gpu vectorization 연산지원(5000개면 5000개 동시엔 연산)한다. => 코딩량, 연산횟수가 줄어듬

tensorflow는 numpy 기반이라 배울 필요가 있다.

 

homo : '1' in 'abc'

hetero : '1' in ['1', b, c]

homo가 데이터 타입 체크를 할 필요가 없기 때문에 더 빠른 연산을 한다.

* a = 1, b = 1. 이건 coercion : 같은 형의 두인자를 수반하는 연산이면 한형의 인스턴스를 다른형으로 묵시적으로 변환해버림

그 외엔 같은 데이터 타입이어야 한다

 

 

linear algebra 선형대수연산

image는 array형태이고, 머신러닝도 빅데이터 병렬연산을 위한 array을 사용함

numpy는 gpu지원안하니깐 tensorflow와 pytorch가 50배나 더빠른 gpu를 사용 

안그래도 c언어보다 python이 20~100배 느린데, 수치연산에 불리하다

하지만 자동화랑 mutable이 너무 좋은걸?

 

그래서 하드웨어 성능을 끌어오는 방법,

compiler를 쓰면 빠름

또 homogenuous 형태를 통해서 속도를 향상시킴

np.ScalarType으로 확인할 수 있음

data type에 따라서 연산 최적화가 달라짐 => backpropagation, 미분 등

미분은 연속만 가능한데 uint8은 불연속이라 float형태로 바꿔줘야 함

=> 중요한 문제라 data type을 항상 알려줌, 타입에 따라서 연산의 방식이 달라지니깐

 

t.gradient()으로 확인

빅데이터는 가장큰 데이터 타입쓰면 됨 => float64

근데 mldl은 gpu연산 라이브러리 들이 32bit 연산에 최적화됨

 

ndarray는 numpy의 class 이름임, 그래서 np.ndarray((2,3))이렇게 만들수있는데 사용자가 불편함

그래서 factory method 방식으로 np.array를 쉽게 쓸수있게 만듬. __repr__로 가능

사실 그냥 똑같은거다 array의 통칭 정도

비어있으면 안되고, 직각 형태로 만들어야 한다.

 

1) , indexing

index로 개별로 가져오기, slicing 뭉탱이로 가져오기

# a[1][1] = a[1,1]

a[1] => 2차원의 1 idx의 list를 가져옴

혹은

a[1,:]으로 하면 동일함

3차원은 a[,,]

 

2) boolean indexing => filter

a[ ( a > 3 ) | ~(a<5) ]

 

3) fancy indexing

재조합해서 만들수있음

a[[1,0]]    a[['aaa', 'bbb']]

 

4) extended fancy indexing

a[[0,1], [1,2]] => 자리값맞춰주면 각각 가져옴

 

5) ellipsis ...

1 이상의 :을 대체, 2차원에서는 별로 의미없음

a[,:] = a[,...]

a[:,:] = a[...]

 

# mutable 개념은 언제 중요할까?  선언 vs 얕은 복사 vs 깊은복사

- a = b # 이렇게 array 선언하면 memory를 공유함

그래서 a 수정하면 b도 수정됨

- a = b[:], b[...]#이렇게 하면 내용을 가져옴 => 얕은 복사, 메모리는 다르니 다른 객체이지만 영향을 받음 id() 다른 메모리 할당

- a = b.copy() # copy도 내용을 가져옴 => 원본과 분리되어 메모리 별도로 받음 : deepcopy()

numpy에만 있고 tensorflow에는 없는 개념임

이제 무조건 copy되도록

 

img의 데이터 운용은 좀더 복잡하다

a[:,:,:] 이 (60000, 28, 28)이면

a[10000:50000,:,:]으로 일부 때오거나

a[:,:14,:14]으로 일부 자르거나 할수있다.

 

shape

a = np.array([1,2,3])

a[:,none] 하면 차원을 증가시킴

[[1],[2],[3]] : none은 여기서 np.newaxis, tf.newaxis와 같다

반대는 np.squeeze(bb)

 

 

 

 

 

 

img 자체는 array로 구현함

import matplotlib.pyplot as plt

plt.imshow(x_train[0], cmap='gray')

 

2차 흑백 이미지

0이니깐 검은색으로 표

 

현 명암만 가능

좌표값의 색상정보

(6,6,1)

1이 0 ~ 255임

 

3차 색깔 이미지 

RGB방식으로 값을 주면 가능 0,0 => [R, G, B]

x_train[0]=> (6,6,3) => 값이 3개가 나옴

6x6이 3장 겹쳐진 상태임, 3이 [R,G,B]형태임

 

4차 영상이미지

개별의 dataset이냐 연속된 이미지인가

x_train => 3000, 6, 6, 3

 

 

 

 

 

 

 

 

 

 

 

 

 

반응형
728x90
반응형

합성, 결합

composite pattern은 남의 클래스를 내가 가지고, 일을 함 

여러 instance를 하나의 명령어로 할 수 있고, 유지보수에 좋다.

또 이게 상속을 대신할 수 있음

잘안쓰는 것은 어려워서지 아예 안쓴다는 것이 아님

그래서 composite을 이용해서 상속을 대신하는 기법

 

내 클래스에 남의 instance를 가져올수있음

inheritance를 대신 씀, 활용 중 하나다

class A :
	pass
    
class C :
	pass

class B :
	def __init__(self) :
    	self.a = A()
		self.c = B();
        
	def multi(self) :
    	return self.a * self.b;
        # 이렇게 하면 상속

composite은 매우 밀접하게 연결짓는 것이라 유지보수면에서 힘든 면이 있다.

예를 들어 상속한 어미 클래스가 바뀌면 상속된 자식 클래스가 다 바뀌어버리는 경우가 있다.

 

그래서 느슨한 연결을 하는데 어떤 instance의 변수를 타 instance의 return 값으로 줘버리면

상속을 하지 않았지만 똑같이 만들 수 있다.

overiding 한다 하면

 

다른 composition 방식으로 __getattr__을 쓰면 된다.

만약 참조가 안되는 instance나 변수를 불러내면 attribute error가 나옴 

그런데 __getattr__를 쓰면 try : attribute error 로 받을 수 없는 값을 처리해줄 수 있다.

거기에 return getattr(다른 class)로 attributor를 이름으로, 지정된 값을 대신 불러다 줌

 

한편 _ 이름 규칙

1. 외부로 import 안됨, 내부 사용 

2. 국제화(internalization) 사용

3. name_이면 numpy 등 python과 충돌 방지용 

4. for i, _ in enumerate([1,2,3]) : 처럼 쓰지 않을 때 관례처럼

5. __name mangling private내부적으로 사용

6. _ : interactive last result : _이전의 마지막 out을 가져옴

7. __x__ : double underbar (dundu / magic method)

* sklearn은 __all__ = [a] *할 때 지정할 수 있음 => 사용자 편하라고 

혹은 __slot__으로 접근방지할 수있음

 

 

import sys

sys.path로 경로 순서대로 지정됨 확인 필요

 

 

getattribute => 아래와 같이 값이 있으면 값을 조작하는 것

setter 값을 선언 g.a = 1

getter 값을 봔환 g.a

deleter 값을 봔환 del g.a

 

'''

descriptor는 값을 사용못하게 하거나 숨기거나 할 때 사용

else [if else, for/while else, try]

from [import, raise, yield]

as [import, except, with]

import antigravity

import this

42

'''

# descriptor 만드는 3가지 방법

1. composite 방법

만들어진 class의 내용을 외부에서 조작할 수 있음

2. name = property(instance, instance) 지정할수있음

3. @property

@name.setter  / deleter로 지정할수있음

단순한 데이터를 처리할때는 = 로 할당할 수 있지만, =로 해야할게 많음

내부적으로 처리해야하는데 descriptor로 처리할 수 있다.

 

# meta class ★ class의 class

기능이 변경되면 meta class임

프레임워크 개발, 데이터를 조작할때 씀

class를 무제한으로 만들수있는데 1개의 instance만 만들수있게 class 기능 변경

type(1) => int

type(int) => type

meta class를 상속해서 meta class를 만듬

 

없으면 super().__call__()로 만들고, 있으면 기존의 instance를 return 해라 => 클래스의 기능을 변경함

class 지정해놓고 class 객체로 변수 선언하면 같은 값임

id()으로 확인하면 메모리 지정이 동일함

 

# abstract

공통된 규칙을 따르고 차이점만 새로 구현한다.

tensorflow가 a기능 제공하고 b,c 규칙을 따라야 한다하면

abstract 규칙을 공개해서 이거 따라야만 한다.

예를들어 from sklearn.naive_bayes import something이고

naive_bayes의 파생형들이 있다면, NB의 기본 규칙이 있고, 파생형들을 만들고 규칙을 공개하면 다른 사람들이 어디서든지 보고 따라할 수 있음 => 파생형이지만 쓰고 싶으면 행동의 규칙을 따라야 한다.

class ss(tf.keras.utils.sequence) :

     pass

dir(ss) => 동일한 기능이 나옴

print(inspect.getsource(기능))

=> abstractmethod있으면 규칙 따라야 함.

 

class A(metaclass = NBmeta) :

     @abstractmethod   => 제약 만듬

     def a(self) :            => 규칙

          pass

class B(A) :

     def a(self) :            => 규칙을 똑같이 만듬

         print('a')

b = B()  # notimplement error X

 

python에서도 이게 너무 어려우니깐 사람들 쓰라고 ABC쓰면 metaclass = 와 기능이 같음

ABC는 추상클래스이다.

 

 

# 상속 체계 확인

dir() 에 안보여도 metaclass, type안에 해봐서 mro를 쓸 수 있다.

.mro()

.__mro__

부모객체 확인 .__bases__

=> functional

마찬가지로 dir에서 확인할 수 없지만 metaclass에 정의되어있어서 가능 two track

 

# class 기능변경가능 => metaclass

# . descriptor

# 모든 연산자 기능 변경가능 => operator overloading

모든 연산자는 그에 대응하는 dundu(magic / special)가 있음

모든 연산자는 그에 대응하는 function이 있음 <- 함수형 프로그래밍 지원

3+4,

함수로하면 from operator import add, sub, mul

add(3, 4)

함수를 사용하는 이유는 인간은 연산자가 명확하지만 기능을 바뀔수가 있어 컴퓨터는 함수형이 의미를 명확하게 전달함

=> layers 도 함수형 연산함, 목적에 맞게 변형된 함수를 만들면 최적화에 좋다

 

 

 

 

 

 

 

 

 

 

 

반응형
728x90
반응형

Python

Python에서는 모든 것이 객체다

함수도 객체 파일도 객체 가능한 모든 것들이 객체

Object (type)의 내부 정보를 알고 싶을 때

1. type 2. dir 3. id 4. vars

이름/식별자를 불렀을 때 내부적으로 작동하는 것들

1. REPR 2. STR

class A:
  def __repr__(self):
    return 'REPR'

  def __str__(self):
    return 'STR'
a = A()
a # 이름을 부르면 __repr__가 호출된다 
# REPR

print(a) # 이름을 print하면 __str__이 호출된다 / escape를 처리해준다 
# STR

※ self를 붙이는 이유

class A: 
  def __init__(this):  # self 대신 다른 것 사용 가능 그러나 pep8에서 self 사용 권장 
    print('a') 
  def __call__(self):  # 객체 지향에서 self는 인스턴스(object)를 뜻한다  
    print('call')
  def aa(self):
    print('aa')

# python 내부에서 c기반으로 작동하기 때문에 (c는 method 개념이 없다) 이와 같이 만들어 졌다 
a = A()
A.aa(a) # 어떤 인스턴스에 대해서 사용할 것인가 인자가 필요 / version 1 / 주체가 class A 
a.aa(a) # 인스턴스가 주체이고 싶을 때 / argument가 중복되기 때문에 a 생략하게 만들었다 
a.aa()
 import itertools 
 dir(itertools) #__package__ / dir은 객체의 모든 attribute를 조회 함수이다 
 ['__doc__',
 '__loader__',
 '__name__',
 '__package__',
 '__spec__',
 '_grouper',
 '_tee',
 '_tee_dataobject',
 'accumulate',
 'chain',
 'combinations',
 'combinations_with_replacement',
 'compress',
 'count',
 'cycle',
 'dropwhile',
 'filterfalse',
 'groupby',
 'islice',
 'permutations',
 'product',
 'repeat',
 'starmap',
 'takewhile',
 'tee',
 'zip_longest']
type(itertools) # module이라는 데이터 타입 
# module

id(itertools)
# 140663255600208

dir(int) # 클래스도 객체 이다 
['__abs__',
 '__add__',
 '__and__',
 '__bool__',
 '__ceil__',
 '__class__',
 '__delattr__',
 '__dir__',
 '__divmod__',
 '__doc__',
 '__eq__',
 '__float__',
 '__floor__',
 '__floordiv__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__getnewargs__',
 '__gt__',
 '__hash__',
 '__index__',
 '__init__',
 '__init_subclass__',
 '__int__',
 '__invert__',
 '__le__',
 '__lshift__',
 '__lt__',
 '__mod__',
 '__mul__',
 '__ne__',
 '__neg__',
 '__new__',
 '__or__',
 '__pos__',
 '__pow__',
 '__radd__',
 '__rand__',
 '__rdivmod__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__rfloordiv__',
 '__rlshift__',
 '__rmod__',
 '__rmul__',
 '__ror__',
 '__round__',
 '__rpow__',
 '__rrshift__',
 '__rshift__',
 '__rsub__',
 '__rtruediv__',
 '__rxor__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__sub__',
 '__subclasshook__',
 '__truediv__',
 '__trunc__',
 '__xor__',
 'bit_length',
 'conjugate',
 'denominator',
 'from_bytes',
 'imag',
 'numerator',
 'real',
 'to_bytes']

객체 지향

[객체지향의 4가지 특성]

1. 캡슐화

- 실제 구현내용을 감추는

- 재활용 가능

- 파이썬에서는 기본적으로 외부에서 클래스 접근 가능

- descriptor로 클래스 접근을 막을 수도 있다

2. 추상화

- 필요로 하는 속성이나 행동을 추출하는 작업

- 구체적인것과 추상적인 것을 분리시킨다

- 특징을 뽑아 객체로 구현

3. 상속

- 추상화의 반대

- 추상화한 클래스를 물려받아 구체적으로 원하는 대로 바꾸어 사용 가능

4. 다형성

- 다양한 결과를 있다

Subclass(Inheritance)

'장점'

- 부모 클래스의 기능을 재사용

-부모의 기능을 그대로 사용한다

- 부모의 기능을 일부만 사용한다

- 부모에게 없는 기능을 추가 가능하다

'단점'

- 부모가 바뀌면 같이 바뀐다

# composition 방식으로 inheritance 방식 대체 가능하다

duck typing, composition으로 사용해도 상속받은 것과 유사하게 사용 가능하다

 

class A:
  a = 1

class B(A): # Delegat(위임/대리자)
  pass 
  
id(A.a) == id(B.a)
# True

A.a is B.a # B에 없으면 부모의 것을 불러오기 때문에 같아진다 
# True

Python의 모든 객체는 object로 부터 상속 받는다

class A(object, metaclass=type):
  pass
  
dir(A)
['__class__',
 '__delattr__',
 '__dict__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__le__',
 '__lt__',
 '__module__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__']
dir(A.__class__)
['__abstractmethods__',
 '__base__',
 '__bases__',
 '__basicsize__',
 '__call__',
 '__class__',
 '__delattr__',
 '__dict__',
 '__dictoffset__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__flags__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__instancecheck__',
 '__itemsize__',
 '__le__',
 '__lt__',
 '__module__',
 '__mro__',
 '__name__',
 '__ne__',
 '__new__',
 '__prepare__',
 '__qualname__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasscheck__',
 '__subclasses__',
 '__subclasshook__',
 '__text_signature__',
 '__weakrefoffset__',
 'mro']

다중 상속

다중 상속이란 2개 이상 클래스를 상속하는 것을 말한다

python은 다중 상속을 지원하는 언어이다

다중 상속은 편한 기능이지만 다이아몬드 문제가 발생할 수 있다

단일 상속인 프로그래밍 언어들이 많음(Java)

class A:
  x = 1

class B:
  x = 2 

class C(A,B):
  pass

# method resolution order (python2.2 부터 지원)

C.mro()
# [__main__.C, __main__.A, __main__.B, object]

C.__mro__
# (__main__.C, __main__.A, __main__.B, object)

C.x # C에 없어서 그 다음 우선순위인 A에서 찾는다 
# 1

다이아몬드 문제

class A:
    pass

class B(A):
    pass

class C(A):
    pass

class D(A, B):
    pass

# 이런 경우 메소드의 실행순서를 정할 수 없다 
# TypeError: Cannot create a consistent method resolution order (MRO) for bases A, B
class A:
    pass

class B(A):
    pass

class C(A):
    pass

class D(B, C):
    pass
    
D.mro() # linearization / tail recursion elimination => linearization
# [__main__.D, __main__.B, __main__.C, __main__.A, object]

dir(D) # metaclass 
['__class__',
 '__delattr__',
 '__dict__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__le__',
 '__lt__',
 '__module__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__']

MRO (Method Resolution Order)

메소드 실행 순서를 확인하는 클래스 메소드(인스턴스로 사용 불가)

class A:
    def __init__(self):
        print('A')

class B(A):
    def __init__(self):   #overriding (parent와 가능한 parameter 일치)
        A.__init__(self) 
        print('B')

class C(A):
    def __init__(self):
        A.__init__(self)
        print('C')

class D(B, C):
    def __init__(self):
        B.__init__(self)   # __init__ 함수 
        C.__init__(self)
        print('D')

d = D()  # A가 중복해서 실행되는 문제가 발생한다 
# A
# B
# A
# C
# D

D.mro()
# [__main__.D, __main__.B, __main__.C, __main__.A, object]
import tensorflow as tf 

model = tf.keras.models.Sequential()

model.__class__.mro()
[keras.engine.sequential.Sequential,
 keras.engine.functional.Functional,
 keras.engine.training.Model,
 keras.engine.base_layer.Layer,
 tensorflow.python.module.module.Module,
 tensorflow.python.training.tracking.tracking.AutoTrackable,
 tensorflow.python.training.tracking.base.Trackable,
 keras.utils.version_utils.LayerVersionSelector,
 keras.utils.version_utils.ModelVersionSelector,
 object]

Super - 중복 실행을 방지하는 방법

super는 상속을 전부 실행하지 않는다

class A:
    def __init__(self):
        print('A')

class B(A):
    def __init__(self):     
        super().__init__()  #super()는 부모의 인스턴스다 / python3 방식이다 
        print('B')

class C(A):
    def __init__(self):  
        super(C, self).__init__()        # __init__은 메소드 이다 / python2, python3 방식이다 
        print('C')

class D(B, C):
    def __init__(self):
        super().__init__()
        print('D')
        
d = D()
# A
# C
# B
# D

D.mro()
# [__main__.D, __main__.B, __main__.C, __main__.A, object]

실행 순서는 D -> B -> C -> A 인데 출력 결과는 왜 반대일까? 그 이유는 바로 super사용시 stack에 들어가기 때문이다

※ Stackoverflow

def fib(n):
  return fib(n-1) + fib(n-2) if n>2 else 1  # 재귀적으로 계산되는 중간 과정은 stack영역에 저장된다 => 중간 과정에서 중복되는 연산이 많이 쌓인다 => stack에 많이 쌓여 넘치는 것을 stackoverflow 
                                            # 그러나 python에서는 overflow가 없다 
fib(10) 
# 55
import sys
sys.maxsize
# 9223372036854775807

sys.maxsize + 1 # 정수형은 stackoverflow가 없는 대신에 내부적으로 현재 남아있는 만큼의 가용 메모리를 모두 수 표현에 끌어다 쓴다. 따라서 정수형 연산은 속도(성능)가 매우 느리다 
# 9223372036854775808

sys.float_info
# sys.float_info(max=1.7976931348623157e+308, max_exp=1024, max_10_exp=308, min=2.2250738585072014e-308, min_exp=-1021, min_10_exp=-307, dig=15, mant_dig=53, epsilon=2.220446049250313e-16, radix=2, rounds=1)

0.1 + 0.1 + 0.1 
# 0.30000000000000004

1.7976931348623157e+308  == 1.7976931348623157e+308 + 1 # 근사적으로 계산하기 때문에 속도가 빠르다 
# True

a == a + 1 # infinity

 

Duck Typing

미운오리새끼 이야기에서 유래가 되어 오리가 아닌데 오리처럼 행동을 하면 오리라고 간주한다는 개념이다

타입을 미리 정하지 않고 실행되었을 때 해당 Method들을 확인하여 타입을 정한다

 

'장점'

- 타입에 대해 자유롭다

- 상속을 하지 않아도 상속을 한것처럼 클래스의 메소드 사용이 가능하다

'단점'

- 원치 않는 타입이 들어 경우 오류가 발생할 있다

- 오류 발생시 원인을 찾기 어려울수 있다

- 호환성 떨어짐 => 추상 클래스로 대체

- 코딩 많이 해야

Composition

상속을 하지 않고 클래스내에 객체를 불러와 다른 클래스의 일부 기능을 사용하는 방법.

상속의 위험부담을 줄일 수 있다

class SecurityDoor:
    locked = True

    def __init__(self, number, status):
        self.door = Door(number, status) # Door의 객체를 갖게 한다

    def open(self):
        if self.locked:
            return
        self.door.open()

    def __getattr__(self, attr):        # try except와 비슷
        return getattr(self.door, attr) # Door의 attr를 가져와라 (상속을 비슷하게 사용)


class ComposedDoor:
    def __init__(self, number, status):
        self.door = Door(number, status)

    def __getattr__(self, attr): # 없으면 가져와라
        return getattr(self.door, attr) # 바꾸지 않고 불러와서 사용할 때

Meta class

type => 모든 클래스의 행동을 결정해준다 클래스의 행동을 바꾸고자 할때 사용하는 클래스

 

type('int2', (int,), {}) # __main__ 메소드가 있다면 현재 내 작업 공간에서 만든 것이라는 의미이다 
# __main__.int2

a = type('int2', (int,), {})
type(a)
# type
class A:  # attribute(클래스안에 정의된 모든 것) vs property (descriptor)
  pass

%whos # 현재 작업 공간에 어떤 객체가 있는가 확인  할때 사용 
#
Variable    Type      Data/Info
-------------------------------
A           type      <class '__main__.A'>
a           type      <class '__main__.int2'>
drive       module    <module 'google.colab.dri<...>s/google/colab/drive.py'>
itertools   module    <module 'itertools' (built-in)>

%who_ls
['A', 'a', 'drive', 'itertools']

%lsmagic
Available line magics:
%alias  %alias_magic  %autocall  %automagic  %autosave  %bookmark  %cat  %cd  %clear  %colors  %config  %connect_info  %cp  %debug  %dhist  %dirs  %doctest_mode  %ed  %edit  %env  %gui  %hist  %history  %killbgscripts  %ldir  %less  %lf  %lk  %ll  %load  %load_ext  %loadpy  %logoff  %logon  %logstart  %logstate  %logstop  %ls  %lsmagic  %lx  %macro  %magic  %man  %matplotlib  %mkdir  %more  %mv  %notebook  %page  %pastebin  %pdb  %pdef  %pdoc  %pfile  %pinfo  %pinfo2  %pip  %popd  %pprint  %precision  %profile  %prun  %psearch  %psource  %pushd  %pwd  %pycat  %pylab  %qtconsole  %quickref  %recall  %rehashx  %reload_ext  %rep  %rerun  %reset  %reset_selective  %rm  %rmdir  %run  %save  %sc  %set_env  %shell  %store  %sx  %system  %tb  %tensorflow_version  %time  %timeit  %unalias  %unload_ext  %who  %who_ls  %whos  %xdel  %xmode

Available cell magics:
%%!  %%HTML  %%SVG  %%bash  %%bigquery  %%capture  %%debug  %%file  %%html  %%javascript  %%js  %%latex  %%perl  %%prun  %%pypy  %%python  %%python2  %%python3  %%ruby  %%script  %%sh  %%shell  %%svg  %%sx  %%system  %%time  %%timeit  %%writefile

Automagic is ON, % prefix IS NOT needed for line magics.

A # REPR(Representation / 이름을 부르면 표현해 주세요) => 메모리에 저장되어 있는 것 부른다 
# __main__.A

※ Error

1. NameError : 이름이 없을

2. AttributeError : attribute가 없을 기능 자체가 없을 

import tensorflow as tf 
model = tf.keras.models.Sequential()

model.summary() # 실행하는 시점에 따라서 인스턴스 변수가 다르게 갖기 때문에 / 값이 없기 때문에 valueError 
# ValueError: This model has not yet been built. Build the model first by calling `build()` or calling `fit()` with some data, or specify an `input_shape` argument in the first layer(s) for automatic build.

model(tf.constant([[1,2]]))
# <tf.Tensor: shape=(1, 2), dtype=int32, numpy=array([[1, 2]], dtype=int32)>

model.summary()
#
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
Total params: 0
Trainable params: 0
Non-trainable params: 0
_________________________________________________________________

layer = tf.keras.layers.Dense(1) 
layer.summary()
# AttributeError: 'Dense' object has no attribute 'summary'

class A:
  t = 1
  def tt(self):
    print('t')
getattr(A, 't') # A에서 t접근 해주세요 
# 1
a = A()
getattr(A, 'tt')(a)
# 1

변수와 상수가 구분이 있는 언어라면 상수는 재할당 할 수 없다

python은 변수와 상수를 구분하지 않는다

따라서 tensorflow에서는 이를 구분하기 위해 constant와 variable을 추가적으로 만들었다

cc = tf.constant([1,2,3]) # 상수
dd = tf.Variable([1,2,3]) # 변수 
dd.assign_add([2,3,4])
# <tf.Variable 'UnreadVariable' shape=(3,) dtype=int32, numpy=array([3, 5, 7], dtype=int32)>

import numpy as np
x = np.array([1,2,3])
x 
# array([1, 2, 3])

print(x)
# [1 2 3]

Python의 *

1. number *, **

- 3*3 # 곱하기

- 3**3 # 제곱

2. sequence *

3. assignment (binding) : unpacking

- x,y,*z = 1,2,3,4 # unpacking에서 나머지

4. parameter *name

- def x(*b): # 인자의 갯수 제한을 두지 않는다 return b

5. parameter *

- def name(*,a,b): # keyword only *다음엔 keyword를 사용해야 한다

       return a + b # positional 방식 다음에 keyword 방식을 써야 한다

6. parameter **

- def y(**b): # Variable Keyword return b 7. argument *

- def a(*b):

      return b x = [1,2,3,4] a(*x) # argument unpacking

8. argument **

- def aa(**b):

       return b y = {'a': 1, 'b' : 2} aa(**)

9. import에서 모두

- import *

※ def t(a, *b)이런 형태를 자주 쓰는 이유

def t(*a, **b):

     print(a)

     print(b)

# parameter, argument를 정확히 맞출 필요가 없음, 인터페이스의 유연성을 위해

 

def aa(**b):
  return b

y= {'a':1,'b':2}
z= {'c':2,'a':3}
{**y, **z}
# {'a': 3, 'b': 2, 'c': 2}

 

 

반응형
728x90
반응형
!pip install multipledispatch​
xxx = 2

def x():
  xxx = 1 
  print(xxx)

x()
# 1

Python

Callable

Call: 함수를 호출 한다

Python에서 괄호('()') 붙일 있는 것은 callable 이다

, 호출 가능한 함수 또는 객체

1. function(method)

2. class

3. __call__이 정의된 object

LEGB

G: 기본적으로 사용하는 공간은 Global 공간 (global)

B: Builtin이라는 공간에 저장되어 있는 것들

E: Enclosing Function Local, 함수를 내포하는 다른 함수 영역

L: Local, 함수 정의된 지역변수

 

python 공간규칙

1. B에 없으면 G에 있는지 찾는다

2. 상위단계(B쪽으로 갈수록 상위)에서 하위단계로 접근할 없다

3. 하위단계(L쪽으로 갈수록 하위)에서 상위단계로는 접근할 있다

 

변수 찾는 우선순위 => L > E > G > B

xxx = 2

def x():
  xxx = 1 
  print(xxx)
x()
# 1
xxx = 2

def x():
  print(xxx)
x()
# 2

Closure

'함수를 중첩시키고 함수를 리턴하는 함수'. 클로저는 보통 함수를 쉽게 변형할 있는 기법이다 # Higher order function 클래스 안에 __call__이 선언되어 있으면 인스턴스가 호출가능해진다(인스턴스( )/__call__메소드가 호출된다)

사실 함수도 클래스의 객체이며 함수를 호출할 '함수이름( )' 같이 사용할 있었던 이유가 바로 __call__ 메서드가 선언되어 있기 때문이다

def clo(m):
    def inner(n):
        return m + n 
    return inner 

class A:
    def __init__(self, m):
        self.m = m
    def __call__(self, n):
        return self.m + n 
clo(4)(3)
# 7

a = A(3)
a(3)
# 6
import tensorflow as tf 
# 4개의 퍼셉트론에 tf.constant([[1,2],[3,4],[5,6]])값을 계산하는 함수 
tf.keras.layers.Dense(4)(tf.constant([[1,2],[3,4],[5,6]])) 

<tf.Tensor: shape=(3, 4), dtype=float32, numpy=
array([[ 1.57166   , -0.1644063 ,  1.6268642 , -0.73175406],
       [ 2.9206128 , -0.0049057 ,  3.2637818 , -1.5273063 ],
       [ 4.2695656 ,  0.1545949 ,  4.9006996 , -2.3228586 ]],
      dtype=float32)>
class B:
  def __init__(self, m):
    self.m = m
  
  def __call__(self, n):
    return self.m + n 
B(3)(4)
# 7

def __call__(self, n):
  return self.m + n + 1
B.__call__ = x

※ function이라는 데이터 타입도 존재한다

def a(n):
  return n 

type(a)
# function

Callable Method 종류

1. Instance method 2. classmethod 3. staticmethod

위임 개념은 크게 3가지로 볼 수 있다

1. instance 2. class 3. inheritance

 

class T:
  a = 1 # class variable/attribute 클래스 안에서는 식별자를 변수라고 칭한다 
        # attribute 클래스 안에 정의 되어 있는 모든 것을 attribute 라고 한다 
T.a # 클래스가 어떻게 행동해요? 클래스는 메타 클래스의 인스턴스이기 때문이다 
# 1

T.a # 클래스가 어떻게 행동해요? 클래스는 메타 클래스의 인스턴스이기 때문이다 
# {'xx': 1}

b.yy # 존재하지 않는 변수에 접근하면 attributeError를 발생시킨다 
# AttributeError: 'T' object has no attribute 'yy'

b.a # 인스턴스 변수에 없으면 클래스 변수에서 찾는다 (위임)
# 1
class A:
  @classmethod   
  def xx(cls):   # instance / class variable의 생성은 시점에 따라 결정된다 
    cls.t = 1 
    print('cls')
  
  def yy(self):
    print('instance')
 
A.xx # classmethod는 클래스 입장에서 함수가 메소드로 
# <bound method A.xx of <class '__main__.A'>>

A.yy
<function __main__.A.yy>

a = A()
a.xx() # 인스턴스에 없으면 클래스 찾는다
# cls

a.t
# 1
import pandas as pd 
type(pd.DataFrame)
# type

pd.DataFrame.from_dict # classmethod
# <bound method DataFrame.from_dict of <class 'pandas.core.frame.DataFrame'>>
class B:
  def aa(self):
    self.a = 1 
  
  def bb(self):
    self.b = 2   # 인스턴스 변수는 인스턴스가 사용한다 
    
b = B()
b.a
# AttributeError: 'B' object has no attribute 'a'

b.aa() # 함수 실행시점에서 인스턴스 변수에 접근 가능해진다 
b.a
# 1

vars(b)
# {'a': 1}
class test:
  a = 1 

  @classmethod
  def one(cls):
    cls.a = 1 
    print('cls')
  
  def two(self):
    self.b = 2
    
t = test()
t.a
# 1

t.b # 인스턴스 메소드가 실행된 시점 이후에 접근 가능하다 
# AttributeError: 'test' object has no attribute 'b'

t.two()
t.b
# 2

 

 

import tensorflow as tf

from tensorflow.keras.layers import Dense, Flatten, Conv2D
from tensorflow.keras import Model

mnist = tf.keras.datasets.mnist

(x_train, y_train), (x_test, y_test) = mnist.load_data() # 동적 생성 
x_train, x_test = x_train / 255.0, x_test / 255.0


class MyModel(Model):
  def __init__(self):     # callable 연산자 overloading / 역할은 기능을 초기화 한다 
    super(MyModel, self).__init__()
    self.conv1 = Conv2D(32, 3, activation='relu')
    self.flatten = Flatten()
    self.d1 = Dense(128, activation='relu')
    self.d2 = Dense(10)

  def call(self, x):
    x = self.conv1(x)
    x = self.flatten(x)
    x = self.d1(x)
    return self.d2(x)

# Create an instance of the model
model = MyModel()

※ 클래스안의 클래스 메소드와 인스턴스 메소드의 가장 큰 차이점은 클래스를 인스턴스화 했을 때 클래스 메소드 안에 있는 클래스 변수는 바로 접근 가능하지만, 인스턴스 메소드 안에 있는 인스턴스 변수는 인스턴스 변수가 실행된 시점 이후에 접근 가능해진다

class C:
  
  def __new__(cls):
    print('new')
  
  def __init__(self):
    print('init')
    
C()
# new

c = C()
# new

vars(c)
# TypeError: vars() argument must have __dict__ attribute
class C:
  
  def __new__(cls):    # 객체를 반환하는 것
    print('new')
    return super().__new__(cls)
  
  def __init__(self):  # 반환된 객체의 값을 지정해주는 것 / 초기화 용도로 사용 / return이 None이어야 한
    print('init')
    
c = C()  # 파이썬에서 인스턴스 생성은 두 단계로 나누어져 있다 
# new
# init

staticmethod

대형 프로젝트에서 주로 사용한다

class T:
  @staticmethod  # 기본적으로 python의 method는 정의 첫번째 인자 강제, 사용은 첫번째 인자 생략하는 것이 원칙이다 
  def st():      # staticmethos는 클래스 인스턴스와 상관 없이 존재한다 
    print('st')

T.st() # 체계적으로 구조화 할 수 있다 
# st

property, descriptor

descriptor 는 . 행동 결정 (get set del) => 정보 은닉에 사용

class D: # callable 관점에서

  def __init__(self, t):
    self._t = t

  @property # callable을 callable 하지 않게 변경
  def t(self):
    return self._t
  
  @t.setter
  def t(self, t):
    self._t = t

  def __getattribute__(self, x):
    if x == '_t':
      print('접근 안됨')

d = D(6)
d.t 
vars(d)
d._t
# 접근 안됨
class E:
  __a = 1 # mangling

E.__a
# AttributeError: type object 'E' has no attribute '__a'

E._E__a
# 1

 

 

Dispatch

Dispatch는 어떤 메소드를 호출할 것인가를 결정하여 그것을 실행하도록 유도해주는 과정을 말한다

 

python은 singledispatch만 지원한다

, multiple dispatch는 3rd party를 설치하면 구현 가능하다

 

파라미터가 한개면 single

파라미터가 두개 이상이면 multiple

 

len # type builtin_function or method / dispatch를 지원해주는 generic function

len([1,2,3]) -> list 관련 메소드로 dispatch

len((1,2,3)) -> tuple 관련 메소드로 dispatch # dispatch는 tensorflow predict에 숨어있는 개념이다

Single dispatch vs Multiple dispatch

# method overloading
# 다른 프로그래밍 언어에서는 parameter가 다르면 다른 함수로 간주한다 
# python에서는 method overloading을 지원하지 않는다 

def x():
    print('x')

def x(a):
    print('a')

Single dispatch

from functools import singledispatch

@singledispatch # 데코레이터 singledispatch함수에 x함수의 기능이 추가 되는 것이다 
def x(a):
    print(a)

@x.register(int)
def _(a):
    print('int', a)
    
@x.register(float)
def _(a):
    print('float', a)
    
# 데코레이터 관점에서 봤을 때 메소드 이름이 동일해도 서로 다르게 작동할 수 있다    
# 데이터 타입에 따라서 다른 메소드를 실행시킬수 있다

x('a')
# a

x(1)
# int 1

x(1.)
# float 1.0

Tensorflow predict (dispatch 예시)

import tensorflow as tf 
 
model = tf.keras.models.Sequential([
    tf.keras.layers.Dense(2, input_shape=(2,))
])
 
model.predict(tf.constant([[1,2]])) # numpy array type
# array([[-0.9186919, -1.4591577]], dtype=float32)


model(tf.constant([[1,2]])) # Tensor type
# <tf.Tensor: shape=(1, 2), dtype=float32, numpy=array([[-0.9186919, -1.4591577]], dtype=float32)>

Multiple dispatch

!pip install multipledispatch
from multipledispatch import dispatch

@dispatch(int, int)
def add(x, y):
    return x + y

@dispatch(object, object)
def add(x, y):
    return "%s + %s" % (x, y)

add(1,2)
# 3

add('1','2')
# '1 + 2'

※ 용어 정리

__함수이름__ 이러한 형태의 메소드: dundu, magic method, speicial method, 일반적으로 프로그래밍 언어에서는 protocol이라고 한다

 

from itertools import zip_longest
x = zip([1,2,3,4],[4,5,6])
list(x)
# [(1, 4), (2, 5), (3, 6)]

y = zip_longest([1,2,3,4],[4,5,6])
list(y)
# [(1, 4), (2, 5), (3, 6), (4, None)]

 

 

 

반응형
728x90
반응형

Python

#목표: 텐서플로우를 활용하여 직접 구현하는 것을 목표로 한다

명령형 프로그래밍 vs 선언형 프로그래밍

명령형 프로그래밍

프로그래밍의 상태와 상태를 변경시키는 구문의 관점에서 연산을 설명하는 프로그래밍 패러다임의 일종이다. 명령형 프로그램은 컴퓨터가 수행할 명령들을 순서대로 써 놓은 것이다.

ex) 절차지향, 객체지향 프로그래밍

 

선언형 프로그래밍

선언형 프로그래밍은 무엇인가를 작업하기 위한 방법을 정의한다.

Python에서는 선언 대신 정의라는 말을 쓰고 어떤 기능을 미리 만들어 놓고 조합하여 결과를 내는 방식을 따른다

ex) 함수형 프로그래밍

 

함수형 프로그래밍의 특징

1. 코드가 간결해진다

- 내부 구조를 몰라도 input, output만 알면 사용 가능

2. 수학적으로 증명이 가능하다

3. for, while문을 자제하고 iter 사용한다

4. 수학적 증명이 필요하기 때문에 구현이 어렵다

- , python에서는 multi paradiam이기 때문에 적절히 혼용 가능

5. 디버깅, 테스트가 용이하다

6. 모듈성, 결합성이 있다.

7. mutable은 처리하지 않는다

8. vectorization 연산을 지원한다

 

# tensorflow에서 함수형 프로그래밍은 형식적 증명가능성이라는 표현이 나온다

# 이때 형식적 증명가능성이란 이론적으로는 가능하지만 실제로는 안될 가능성도 있다는 의미 이다

# 함수형 프로그래밍은 함수 자체가 수학함수로 만들기 때문에 증명이 가능하고 이론과 구현의 간극을 줄여주기 때문에 데이터 처리에 관한 분야에 적합하다고 할 수 있다

 

┌─ Mutable # 추가, 삭제가 가능한 경우 / 특징 : 메모리 번지 안바뀜, 재할당할 필요없음

└─ Immutable # 추가, 삭제가 불가능한 경우 / 특징 : 재할당으로 메모리 번지 바뀜

Container

┌─ Homogeneous # 요소들이 서로 같은 타입인 경우

└─ Heterogeneous # 요소들이 서로 다른 타입이 가능한 경우

(요소가 1 이상인 데이터 구조)

Sequence

┌─ Indexing # 요소를 하나씩 출력

└─ Slicing # 요소를 한번에 여러개 출력

(순서가 있음)

Lookup

┌─ Mapping hash # key값과 value를 갖는 Dictinary가 갖는 특징

└─ set # 순서가 없는 고유한 원소들의 집합

(key값으로 이루어진 데이터 구조)

 

Container (container이면 전부다 iterable하다 , 순서대로 하나씩 뽑아 있는 데이터 타입)

- Homogeneous ex) ndarray

- Heterogeneous ex) list

- sequence ex) list

- non sequence ex) set

- mutable ex) list

- immutable ex) tuple

 

x = [1,2,3]
x = iter(x)
'__iter__' in dir(x) # __iter__가 있기 때문에 iterable하다는 의미이다 
# True

'__next__' in dir(x) #__next__가 있다면 iterator라는 의미이다
# True

from collections.abc import Iterable, Iterator
set(dir(Iterator)) - set(dir(Iterable)) 
# {'__next__'}

set(dir(Iterable)) - set(dir(Iterator))
# set()

Iterator

Iterator는 데이터 스트림을 표현하는 객체, next()메소드를 사용하여 다음 요소를 가져온다

Iterator를 활용하여 Lazy Evaluation 방법을 사용한다

 

Lazy Evaluation

- 계산 결과 값이 필요할 때까지 계산을 늦추는 방식이다

- next 실행하는 순간 연산이 시작되고 호출한 값만 메모리에 할당되므로 메모리를 효율적으로 사용할 있게된다

- 내부적으로 최적화 되어 있어 속도가 빠르다

- 방대한 데이터를 다룰때 효율적인 처리를 있다

 

주의 iterator는 scope를 초과하면 StopIteration 에러가 뜬다

 

모든 iterable은 iterator로 만들 수 있다

a = [1,2,3]
b = iter(a) # __next__ 가 생기고 next를 사용할 수 있게 된다 (iterators)
next(b)
# 1

for i in [1,2,3]: # for문에 들어간 iterable한 객체는 iterator로 변환후 사용된다 
    print(i) 
# 1
# 2
# 3

Lazy Evaluation

Lazy Evaluation은 계산 결과 값이 필요할 때까지 계산을 늦추는 방식이다 (next로 호출하는 순간 메모리에 올라간다) Lazy Evaluation은 속도가 느리다는 단점이 있지만 파이썬에서는 내부적으로 최적화 되어 있어 속도가 빠르다 메모리의 효율성을 위해 사용한다

a = [1,2,3]
b = iter(a) # next 호출하기 전까지 메모리에 올라가지 않는다 / next호출 시에만 실행 
b[0]  # iterator로 만들면 sequence한 성질을 잃는다 
# 'list_iterator' object is not subscriptable
a = range(10)
b = iter(a)
next(b)
# 0

list(b)
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

next(b) 
# list를 통해 나머지 요소가 전부 출력 되었으므로 더 이상 남아있는 데이터가 없어서 StopIterationError가 발생한다 
# StopIteration:

 

Generator

Iterator를 생성해주는 Function, 그리고 일반 함수와 비슷해 보이지만 Generator는 yield 포함한다는 점에서 차이가 있다

Iterator와 generator의 차이점은 generator에서는 immutable 데이터 타입으로만 생성 가능한 점이다

특히 lazy evaluation 기법을 사용하기 때문에 메모리 사용이 효율적이고 속도가 빠르다는 장점이 있다

가지 방법으로 만들 있다

1. generator 표현식(tuple)

2, yield

주의 generator는 scope를 초과하면 StopIteration 에러가 뜬다

Tuple 방식

a = (x for x in range(10)) # 메모리 번지가 표시될 경우 iterator 또는 generator인 경우가 많다 
a
# <generator object <genexpr> at 0x7fad65698cd0>

Yield 방식

def x():
    yield 1
    yield 2
y = x()
next(y)
# 1

next(y)
# 2
%%writefile a.txt
abcdefg 
123123
dfskjfdskjl

# Writing a.txt
b = open('a.txt')
next(b)
# 'abcdefg \n'

b.close()
next(b) # 파일 읽기를 종료했기 때문에 더 이상 불러올 수 없다
ValueError: I/O operation on closed file.

Comprehension

여러 개의 데이터를 동시에 만들거나 변화시킬 수 있는 구문이다

Iterable한 객체를 생성하기 위한 방법

[종류] 1. List 2. Set 3. Dictionary

 

import tensorflow as tf 
tf.keras.preprocessing.image.I

 [x for x in range(10)]
 # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
 
 [(x, y) for x in range(5) for y in range(6,10)] # 여러 개의 데이터를 동시에 생성할 때 
 [(0, 6),
 (0, 7),
 (0, 8),
 (0, 9),
 (1, 6),
 (1, 7),
 (1, 8),
 (1, 9),
 (2, 6),
 (2, 7),
 (2, 8),
 (2, 9),
 (3, 6),
 (3, 7),
 (3, 8),
 (3, 9),
 (4, 6),
 (4, 7),
 (4, 8),
 (4, 9)]
 
 [x for x in range(10) if x%2 ==0] 
 # [0, 2, 4, 6, 8]
 [x+1 for x in range(5)] # 기존에 있는 데이터를 변경시킬 때

Accumlation pattern

# 초기 값에서 값을 누적하며 저장하는 방식 
temp = 0 
for i in range(1, 11):
  temp += 1
temp
# 10
temp = []
for i in range(10):
  temp.append(i)
temp
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
import time
from functools import wraps 

def timeit(fn):
    @wraps(fn)
    def inner(*args, **kwargs):
        start_time = time.time()
        retval = fn(*args, **kwargs)
        duration = time.time() - start_time
        print("%s : %2.2f sec" % (fn.__name__, duration))
        return retval
    return inner
    
@timeit
def acc(n):
  temp = 0
  for i in range(n):
    temp += i
  return temp
acc(100000000)
# acc : 6.47 sec
# 4999999950000000

@timeit
def acc2(n):
  temp = 0
  temp = [temp + x for x in range(n)]
  return temp
# SyntaxError: invalid syntax

acc2(100000000)
# acc2 : 8.64 sec
# 4999999950000000

Python를 활용한 직업군

1. Automation

2. Data(big, ai)

3. Web

Data를 다루는 사람들의 관점에서는 comprehension은 쓰지 않는다.

왜냐하면 comprehension은 메모리에 한꺼번에 올리기 때문에 Bigdata에서는 적합하지 않다.

 

함수의 특징

1. return 반드시 있어야 한다 (return 하나이다)

- python에서는 return 생략하면 None 반환하도록 되어 있다

2. 함수 안에 다른 함수를 선언할 있다

3. Global, Local

- 함수 안에 없는 값을 return 하게 경우 가까운 Global 식별자를 return

- Global 식별자이름 하게 되면 접근, 수정이 가능하다

- 함수 밖에서 함수 안의 식별자에 접근, 수정이 불가능하다

 

First class function

함수를 값으로 사용할 수 있다

 

a = print
a('function')
# function

b = lambda x: x + 1 
b(4)
# 5

Higher order function

함수를 리턴값으로 쓰고, 함수를 인자로 쓰는 함수

First class function VS Higher order function

프로그래밍 언어가 first class function을 가진다는 말은 해당 프로그래밍 언어에서 함수를 값으로 취급 한다는 의미이다 Higher order function은 하나 이상의 함수를 인자로 받을 있고 함수를 반환 있는 함수를 의미한다

 

Higher order(고차)라는 개념은 수학적 의미의 함수와 같이 일반적으로 함수에 적용할 있고,

First class(일급)라는 개념은 프로그래밍 언어의 기능에만 연관이 있다

수학적 의미가 아닌 프로그래밍 관점에서 first class function을 지원하거나 지원하지 않거나 라고 말하는 것이 자연스러운 표현이다

 

따라서 Higher order function을 지원하지 않지만 first class function을 가진 언어 또는 higher order function은 지원하지만 first class function을 갖지 않는 언어가 존재할 있는가 싶을 만큼 둘은 밀접한 관련이 있다

 

Recursion

함수가 자기 자신을 호출하는 방법을 재귀라고 한다

※ 함수형 패러다임에서는 tail recursion elimination 기법을 제공하지만, python에서는 지원하지 않는다

 

def rr(x):
    if x==1:
        return 1
    return rr(x-1)*2
rr(6)
# 32

def fibb(n):
  x = 0
  1 =

Tail recursion elimination

Recursion(재귀)용법을 사용하면 함수가 자기 자신을 호출해야하기 때문에 호출할때 마다 중간 값을 저장하기 위한 메모리가 필요하고 호출 횟수가 늘어날 수록 실행 시간이 비약적으로 늘어나는 단점이 있다

그래서 이렇한 단점을 보완한 것이 바로 tail recursion elimination이다

 

반적으로 재귀 호출시 마지막(꼬리부분)에서 호출하는 경우를 반복문으로 바꾸어 결과를 누적시킴으로써 메모리 공간상의 효율도 챙기고 실행 시간도 많이 줄일수 있게 된다

# 일반적인 재귀용법 
def sumrange(m, n):
    if m <= n:
        return m + sumrange(m+1, n)
    else: 
        return 0 

sumrange(1,960)
# 461280
# 꼬리 재귀용법
def sumrange(m, n):
    def loop(m, total):
        if m <= n:
            return loop(m+1, m+total)
        else:
            return total
    return loop(m, 0)
sumrange(1,960)
# 461280

Map & Filter & Reduce (Higher order functioin)

Map

iterable에 있는 모든 요소에 function을 적용하여 결과를 반환한다

map 사용하면 lazy evaluation로 진행해서 메모리를 크게 절약할 있다

연산 결과는 map iterator 객체로 리턴한다

- Deep learning / Big data에서 주로 사용한다

def a(x):
    return x + 1

list(map(a,[1,2,3]))
# [2, 3, 4]

a = map(lambda x : x+1, [1,2,3,4]) # 로직에 집중할 수 있다는 장점이 있다 
'__next__' in dir(a)
# True

list(a)
# [2, 3, 4, 5]
import seaborn as sns
tips = sns.load_dataset('tips')
tips.tip.apply(lambda x:x+1) # 많은 데이터를 한번에 전처리 할때 자주 쓰인다 

0      2.01
1      2.66
2      4.50
3      4.31
4      4.61
       ... 
239    6.92
240    3.00
241    3.00
242    2.75
243    4.00
Name: tip, Length: 244, dtype: float64

Filter

predicate function 함수

iterable객체에 있는 요소중 조건에 맞게 True or False 되돌려 주는 함수

전체 데이터에서 필요한 데이터만 뽑아내는 함수

 

def b(x):
    return x > 3

list(filter(b,[1,2,3,4,5,6]))
# [4, 5, 6]

x = filter(lambda x:x>2, [1,2,3,4]) #필요한 데이터만 뽑아낸다 
list(x) 
# [3, 4]

Reduce

iterable객체의 여러개 값을 하나의 값으로 축약하여 표현할 사용한다

machine learning, deeplearning에서 자주쓰는 함수

통계에서 전체 값중에서 하나의 대표 값을 표현하는 경우가 많기 때문에 reduce를 자주 사용한다

 

from functools import reduce

reduce(lambda x,y:x+y,[1,2,3,4,5])
# 15
import tensorflow as tf 
tf.reduce_mean([1,2,3,]) # multiprocessin 기법을 사용할 수 있기 때문에 속도를 향상시킬수 있다 
<tf.Tensor: shape=(), dtype=int32, numpy=2>

def x(a): # 여기서 함수는 x 
  return a

 

파이썬 코드 스타일

PEP8 (Python Enhance Proposal)

파이썬은 PEP8은 파이썬 개선 제안서, 파이썬 코드를 어떻게 구상할 알려주는 스타일 가이드이다

PEP8 참고 문서: https://www.python.org/dev/peps/

# 귀도 반로섬(Guido van Rossum) #자애로운 종신 독재자

식별자 표기법 3가지

1. snake - 단어마다

_ (underscore) 붙여 이어나가는 표기법이다

- 모듈은 표기법을 권장한다

- 내장 함수도 보통 스네이크 표기법을 따른다

- ex) hello_world

2. camel

- 문자는 소문자로 표기하고, 다음 단어의 시작은 대문자로 표기한다

- 보통 파이썬에서는 카멜방식 채택

- 함수명은 표기법을 권장한다. ,소문자 + underscore를 쓰기도 한다

- ex) helloWorld

3. pascal(caps word)

- 문자를 대문자로 표기하고, 다음 단어의 시작도 대문자로 표기한다

- 클래스명은 표기법을 권장한다. , 이미 만들어져 있는 클래스는 소문자로 시작한다

- ex) HelloWorld

## 원활한 의사소통을 위해 pep8을 활용한다

Lambda

대입문과 정의문의 공동 목표는 재사용이었지만,

재사용할 필요가 없는 function이 필요할 사용하는 것이 lambda이다

- lambda 함수 이름이 존재하지 않는다 (익명함수)

- lambda 식처럼 사용 가능하다 (함수식)

- lambda 함수 호출시 사라지기 때문에 stack영역에 저장된다

반응형

+ Recent posts