기본 콘텐츠로 건너뛰기

[ML] 결정트리(Decision Tree) 모델

Convolutional Neutral Network(CNN)

Convolutional Neutral Network(CNN)

CNN

CNN(Convolutional Neural Networks)은 이미지 분류, 객체 감지, 분할 등과 같은 이미지 및 비디오와 관련된 기계 학습 문제를 해결하는 것으로 알려진 딥 러닝 모델 유형입니다. CNN은 학습 가능한 매개변수를 공유하는 함성곱충(convolutional layer)이라는 특수한 유형의 레이어를 사용하기 때문입니다. 가중치 또는 매개변수 공유는 이미지에서 학습할 패턴(예: 가장자리 또는 윤곽)이 이미지의 픽셀 위치와 무관하다고 가정하기 때문에 작동합니다.

CNN의 구성; CONV: 합성곱 연산 → 활성화 함수:ReLU 등 → POOL:풀링연산

적용이유

  • 다층 퍼셉트론 : 이미지를 1차원 텐서로 변환하여 입력층에 입력, 그러므로 공간적/지역적 구조의 정보(spatial/topological information)가 유실
  • 합성곱 신경망: 공간적인 구조 정보를 보존하면서 학습할 수 있는 방법
  • 공간 구조의 정보 : 이미지는 높이, 너비, 채널로 구성된 3차원 텐서
    • 높이는 세로방향의 픽셀 수, 너비는 가로방향을 픽셀 수 채널은 색성분을 의미
    • 예를들어 흑백이미지는 채널수가 1. 각 픽셀은 0~255사이의 값

CNN의 주요포인트는 이미지 전체 보다는 부분에 집중하며 집중한 정보와 주변정보들의 연관성을 찾는 것입니다. 예를 들어 새의 이미지에서 다른 형태들과 두드러진 특징은 부리에 있습니다. 그러므로 부리의 유무로 다른 형태들과의 구분할 수 있습니다. 이러한 이유로 전체를 파악하는 것보다는 부리부분 만을 조사하는 것이 더 효율적일 것입니다. 이와같은 중요부분을 이해하고자 하는 모델이 CNN입니다.

각 새들마다 부리의 위치에 차이가 있습니다. 그러므로 부리의 유무와 함께 주변 부분과의 연관성도 분석을 위한 대상이 됩니다.

이미지는 2차원 행렬로 나타낼 수 있습니다. CNN에는 이미지 행렬의 특정부분을 선택하기 위한 커널(필터)를 고려합니다. 이 커널의 모양과 초기값은 랜덤으로 지정하며 학습을 통해 적정값으로 조정됩니다. 즉, 입력값인 행렬의 모든 영역에 커널을 반복 적용하여 일정한 패턴을 찾는 것이 CNN 모델의 목적입니다.

이 목적을 위해 입력 행렬과 커널간의 동일 차원의 요소들의 곱을 실행합니다. 그러므로 입력행렬과 커널의 형태는 같아야 합니다.이 과정을 합성곱이라 합니다.

다음 코드는 이미지의 높이×너비의 텐서 input과 가중치를 나타내는 kernel 행렬(일반적으로 3 × 3 또는 5 × 5 차원으로 여기서는 3 ×3)의 곱의 합을 나타낸 것입니다. 이미지 5 × 5 차원이므로 3 × 3인 kernel 차원과의 곱 연산을 위해서는 이미지에서 6개의 3 × 3 부분 행렬을 추출할 수 있습니다. 결과로서 각 곱의 합으로 구성된 3 &time;3의 행렬이 반환됩니다.

import numpy as np
import pandas as pd
inPut=np.array([[1,2,3,4,5], [2,1,0,1,2],[3,0,1,1,0],[1,4,1,1,2], [2,1,1,0,0]])
inPut
array([[1, 2, 3, 4, 5],
       [2, 1, 0, 1, 2],
       [3, 0, 1, 1, 0],
       [1, 4, 1, 1, 2],
       [2, 1, 1, 0, 0]])
kernel=np.array([[1,0,1], [1,0,1], [0,1,0]])
kernel
array([[1, 0, 1],
       [1, 0, 1],
       [0, 1, 0]])
result=np.zeros((3,3))
for i in range(3):
    for j in range(3):
        result[i,j]=(inPut[i:(i+3), j:(j+3)]*kernel).sum()
result
array([[ 6.,  9., 11.],
       [10.,  4.,  4.],
       [ 7.,  7.,  4.]])

위 코드의 일부인 첫번째 추출 이미지와 kernel을 곱한것에 대한 진행은 다음 그림 1과 같이 나타낼 수 있습니다.

그림 1.

위 과정과 같이 합성곱에 의한 결과 행렬은 특성맵(feature map)이라고 합니다.

위 과정의 참여하는 각 객체의 차원은 다음과 같습니다.

  • 입력: inr × inc
  • 커널: kr × kc
  • 특성맵: (inr-kr+1) × (inc-kc+1)

Padding과 Stride

위의 특성맵의 차원은 3 × 3으로 입력 차원보다 감소했습니다. 즉, 손실이 발생한 것입니다. 이 손실의 부분을 임의의 값으로 채워줌으로서 입력과 동일한 형태로 변형할 수 있습니다. 이러한 과정을 패딩(Padding)이라 합니다.

위의 경우 입력 행렬의 차원을 7 × 7로 변형함으로서 특성맵의 형태를 5 × 5로 변경할 수 있습니다.

inPut1=np.zeros((7,7))
for i in range(1, 6):
    for j in range(1, 6):
        inPut1[i, j]=inPut[(i-1), (j-1)]
inPut1
array([[0., 0., 0., 0., 0., 0., 0.],
       [0., 1., 2., 3., 4., 5., 0.],
       [0., 2., 1., 0., 1., 2., 0.],
       [0., 3., 0., 1., 1., 0., 0.],
       [0., 1., 4., 1., 1., 2., 0.],
       [0., 2., 1., 1., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0.]])
featureMap=np.zeros((5,5))
for i in range(5):
    for j in range(5):
        featureMap[i,j]=(inPut1[i:(i+3),j:(j+3)]*kernel).sum()
featureMap
array([[ 4.,  5.,  6.,  9.,  6.],
       [ 6.,  6.,  9., 11.,  5.],
       [ 2., 10.,  4.,  4.,  4.],
       [ 6.,  7.,  7.,  4.,  2.],
       [ 5.,  5.,  6.,  4.,  1.]])

위의 과정에서 padding을 적용하지 않은 경우 최종 특성맵은 3 × 3 차원입니다. 이 결과는 커널과 합성곱을 위해 입력 행렬에서 3 × 3 행렬을 각 행과 열을 한칸씩 이동하면서 선택한 결과입니다. 이 특성맵의 차원을 2 × 2로 만들기 위해서는 대상 행렬을 추출하기 위해 두 칸 씩이동하여 선택하여야 합니다. 이와 같이 특성맵의 차원을 조정하기 위해 입력창의 이동 간격을 스트라이드(stride)라고 합니다.

다음은 원시행렬의 상,하, 좌, 우에 padding을 첨가하고(inPut1) stride를 2로 하는 경우의 특성맵입니다.

featureMap2=np.zeros((3,3))
rng=np.arange(0, 7, 3)
m=0
for i in range(1, len(rng)):
    n=0
    for j in range(1, len(rng)):
        featureMap2[m, n]=(inPut1[rng[i-1]:rng[i], rng[j-1]:rng[j]]*kernel).sum()
        n+=1
    m+=1
featureMap2
array([[4., 9., 0.],
       [6., 4., 0.],
       [0., 0., 0.]])

입력 대상과 커널의 합성곱에 의한 특성맵의 형태는 다음과 같이 수학적으로 정리할 수 있습니다.

$$\begin{align}&O_h = \text{floor}\left(\frac{I_h-K_h}{S}+1 \right)\\ &O_w = \text{floor}\left(\frac{I_w-K_w}{S}+1 \right)\\ &O_h = \text{floor}\left(\frac{I_h-K_h+2P_h}{S}+1 \right)\\ &O_w = \text{floor}\left(\frac{I_w-K_w+2P_w}{S}+1 \right)\\ &\begin{aligned}&I_h:\text{입력높이} &I_w: \text{입력너비}\\ &K_h:\text{커널높이} &K_w: \text{커널너비}\\ &P_h:\text{패딩높이} &P_w: \text{패딩너비}\\ &S:\text{Stride}&\\ &\text{floor}:\text{소수점이하 절삭함수}&\\ &O_h:\text{특성맵높이} &O_w: \text{특성맵너비}\end{aligned}\end{align}$$

위 예는 2차원 입력에 대한 것으로 이 과정은 3차원 텐서로 확장될 수 있습니다.

퍼셉트론의 경우는 입력의 모든 요소에 가중치가 적용됩니다. 예를 들어 그림 2에서 나타낸 것과 같이 3 × 3의 2차원 입력은 입력층에 1차원 벡터로 구성되며 가중치는 각 요소에 적에 적용되어야 36개의 가중치가 필요합니다.

그림 2.

그림 2와 같이 퍼셉트론은 전파과정에 입력층의 모든 요소가 가중치가 적용됩니다. 반면에 그림 3과 같이 합성곱 모형에서는 특성맵의 한 원소는 입력이미지에서 커널의 차원과 같은 차원의 원소들을 추출하여 합성곱 연산을 실행한 결과가 됩니다. 즉, 퍼셉트론에서는 은닉층의 유닛 하나의 결과를 위해 입력층의 모든 요소들이 적용되어야 하지만 합성곱 모형에서는 일부의 요소들만 연결됩니다. 합성곱의 결과인 특성맵이 되는 은닉층을 합성곱층(convolution layer)라고 합니다.

그림 3.

CNN의 구조

다층퍼셉트론(MLP)의 경우 완전연결계층(fully coonected layer 또는 Dense layer)로 연결되어 있어 Affine 층과 활성화함수를 통과하는 구조를 갖는 반면에 CNN은 합성곱층과 폴링층(pooling layer)를 활성화 함수 앞뒤에 배치하는 구조를 갖습니다.

Input → Convolution + Activation → Pooling + Convolution + Activation → Pooling → … → Flatten → Dense+classification → Activation → Output

위에서 소개한 것과 같이 Convolution Layer(합성곱 층)에서 특성맵이 생성된 결과를 활성함수에 의해 처리됩니다. 그 이후에 Pooling 층을 통과하게 됩니다.

Pooling은 특성맵의 차원 축소를 위해 실시합니다. 예를 들어 28 × 28 크기의 이미지를 5 × 5 크기의 filter(kenel)로 합성곱을 실시하면 특성맵의 차원은 (28-5+1),(28-5+1)로 24 × 24가 됩니다. 또한 이러한 커널을 10개 적용하고 1차원 벡터화(flatten)하면 5760의 크기로 동일한 갯수의 가중치가 필요하게 됩니다. 이렇게 증가한 요소들의 수는 overfitting(과적합)의 원인이 됩니다. 이를 방지하기 위해 요소들의 갯수를 감소시킬 필요가 있습니다.이 과정을 pooling이라 합니다.

(28-5+1)*(28-5+1)*10
5760

Pooling의 대표적인 방법으로 특성맵에 일정한 구간에서 최대값만을 추출하는 Max Pooling과 그 구역의 평균값을 계산하여 사용하는 Average Pooling이 있습니다. 예를 들어 4 × 4의 특성맵을 2 × 2로 축소하기 위해 각 2행, 2열 씩 구분하여 원 행렬로부터 부분행렬을 생성합니다. 이 분할은 numpy 패키지의 split(x, 부분을 위한 인덱스, axis=0) 함수를 사용하여 다음과정에 의해 실행됩니다.

  1. 행 단위로 분할합니다. np.split(x, index, axis=0)
  2. 1번의 결과 행렬 각각을 열 단위로 분할 합니다. np.split(x, index, axis=1)
  3. 2번의 각 결과 행렬에서 최대값 또는 평균값을 계산하여 행렬로 전환합니다.
40157222$\begin{matrix}40&15\\43&82\end{matrix}$$\begin{matrix}72&22\\75&7\end{matrix}$
438275 7
34499575$\begin{matrix}34&49\\85&47\end{matrix}$$\begin{matrix}95&75\\63&31\end{matrix}$
85476331

위의 분할된 각 부분에서 최대값을 계산한 결과가 Max Pooling입니다.

8275
8595

위의 분할된 각 부분에서 평균값을 계산한 결과가 average Pooling입니다.

4544
53.7566
np.random.seed(2)
featMap=np.random.randint(0, 100, size=(4,4))
featMap
array([[40, 15, 72, 22],
       [43, 82, 75,  7],
       [34, 49, 95, 75],
       [85, 47, 63, 31]])
rowsplit=np.split(featMap, [2])
rowsplit
[array([[40, 15, 72, 22],
        [43, 82, 75,  7]]),
 array([[34, 49, 95, 75],
        [85, 47, 63, 31]])]
colsplit=[np.split(i, 2, axis=1) for i in rowsplit]
colsplit
[[array([[40, 15],
         [43, 82]]),
  array([[72, 22],
         [75,  7]])],
 [array([[34, 49],
         [85, 47]]),
  array([[95, 75],
         [63, 31]])]]
maxPool=np.array([[colsplit[0][0].max(), colsplit[0][1].max()],[colsplit[1][0].max(), colsplit[1][1].max()]])
maxPool
array([[82, 75],
       [85, 95]])
averagePool=np.array([[colsplit[0][0].mean(), colsplit[0][1].mean()],[colsplit[1][0].mean(), colsplit[1][1].mean()]])
averagePool
array([[45.  , 44.  ],
       [53.75, 66.  ]])

위 과정을 함수로 작성하면 다음과 같습니다.

def poolMat(x, nr, nc, option='max'):
    def sameRowSplit(x, nr):
        r, c=x.shape
        if r % nr==0:
            idx=np.arange(0, r, nr)[1:]
        return(np.split(x, idx))  
​
    def sameColSplit(x, nc):
        r, c=x.shape
        if r % nc==0:
            idx=np.arange(0, c, nc)[1:]
        return(np.split(x, idx, axis=1)) 
    
    rer=sameRowSplit(x, nr)
    pr,pc=x.shape
    poolMat=np.zeros((pr//nr, pc//nc))
    if option == 'max':
        for i in range(len(rer)):
            poolMat[i, :]=[j.max() for j in sameColSplit(rer[i], nc)]
    else:
        for i in range(len(rer)):
            poolMat[i, :]=[j.mean() for j in sameColSplit(rer[i], nc)]
    return(poolMat)
maxP=poolMat(featMap, 2, 2, 'max')
maxP
array([[82., 75.],
       [85., 95.]])
averagePool=poolMat(featMap, 2, 2, 'avg')
averagePool
array([[45.  , 44.  ],
       [53.75, 66.  ]])

위와 같이 합성곱-활성화 층과 풀링층의 단계를 반복한 후 나온 결과는 이미지와는 다른 특성값들로 구성된 행렬이 됩니다. 최종적으로 이 특성행렬을 1차원 벡터 데이터로 변형하여 퍼셉트론 또는 다중 퍼셉트론 모형에 대입합니다. 즉, Dense Layer(s)을 만들고 softmax 활성함수를 적용하여 최종 결과물을 출력합니다.

CNN 모델 구축

MNIST 데이터를 대상으로 CNN 모델을 구축합니다.

입력 벡터를 사용하는 대신 입력 텐서는 이제 회색조 MNIST 이미지에 대해 새로운 차원(높이, 너비, 채널) 또는 (image_size, image_size, 1) = (28, 28, 1)을 갖습니다. 이 입력 모양 요구 사항을 준수하려면 학습(train) 및 테스트 이미지의 크기를 조정해야 합니다.

import numpy as np
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Activation, Dense, Dropout
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten
from tensorflow.keras.utils import to_categorical, plot_model
from tensorflow.keras.datasets import mnist
#load mnist dataset
(xtr, ytr),(xte, yte)=mnist.load_data()
#label의 길이 
numLabel=len(np.unique(ytr))
#label을 one-hot vector로 전환 
ytr=to_categorical(ytr)
yte=to_categorical(yte)
ytr[1]
array([1., 0., 0., 0., 0., 0., 0., 0., 0., 0.], dtype=float32)
#입력 벡터의 형태
xtr.shape
(60000, 28, 28)

위 입력 벡터는 (배치, 높이, 너비)의 형태입니다. 이를 (높이, 너비, 채널)의 형태로 전환합니다. 이 데이터의 경우 흑백이므로 채널을 1이 됩니다.

imageSize=xtr.shape[1]
xtr=np.reshape(xtr, [-1, imageSize, imageSize, 1])
xte=np.reshape(xte, [-1, imageSize, imageSize, 1])
#데이터의 표준화
xtr=xtr.astype("float32")/255
xte=xte.astype("float32")/255
xtr[0].shape
(28, 28, 1)

CNN 신경망의 매개변수를 지정합니다. 다음 매개변수에서 filers는 각 층의 출력 크기를 지정하는 것입니다.

inputShape=(imageSize, imageSize, 1)
batchSize=128
kernelSize=3
poolSize=2
filters=64
dropout=0.2

CNN-ReLU-MaxPooling으로 구성한 모델을 구축합니다. Sequential()클래스를 적용합니다.

model=Sequential()
model.add(Conv2D(filters=filters, kernel_size=kernelSize, activation='relu', input_shape=inputShape))
model.add(MaxPooling2D(poolSize))
model.add(Conv2D(filters=filters, kernel_size=kernelSize, activation='relu'))
model.add(MaxPooling2D(poolSize))
model.add(Conv2D(filters=filters, kernel_size=kernelSize, activation='relu'))
model.add(Flatten())
#정규화의 수단으로 dropout 층을 첨가 
model.add(Dropout(dropout))
#출력층은 10차원의 원-핫 벡터
model.add(Dense(numLabel))
model.add(Activation("softmax"))
model.summary()
 Model: "sequential_9"
    _________________________________________________________________
    Layer (type)                 Output Shape              Param #   
    =================================================================
    conv2d_12 (Conv2D)           (None, 26, 26, 64)        640       
    _________________________________________________________________
    max_pooling2d_8 (MaxPooling2 (None, 13, 13, 64)        0         
    _________________________________________________________________
    conv2d_13 (Conv2D)           (None, 11, 11, 64)        36928     
    _________________________________________________________________
    max_pooling2d_9 (MaxPooling2 (None, 5, 5, 64)          0         
    _________________________________________________________________
    conv2d_14 (Conv2D)           (None, 3, 3, 64)          36928     
    _________________________________________________________________
    flatten_4 (Flatten)          (None, 576)               0         
    _________________________________________________________________
    dropout_4 (Dropout)          (None, 576)               0         
    _________________________________________________________________
    dense_3 (Dense)              (None, 10)                5770      
    _________________________________________________________________
    activation_1 (Activation)    (None, 10)                0         
    =================================================================
    Total params: 80,266
    Trainable params: 80,266
    Non-trainable params: 0
    _________________________________________________________________

원-핫 벡터를 위한 비용함수로 categorial_crossentropy, 최적화를 위해 adam, 분류작업의 신뢰도는 accuracy로 나타냅니다.

model.compile(loss="categorical_crossentropy", optimizer='adam', metrics=['accuracy'])

모델 훈련은 .fit() 매소드를 사용합니다.

model.fit(xtr, ytr, epochs=10, batch_size=batchSize)
Epoch 1/10
    469/469 [==============================] - 55s 111ms/step - loss: 0.5883 - accuracy: 0.8134
    Epoch 2/10
    469/469 [==============================] - 53s 112ms/step - loss: 0.0699 - accuracy: 0.9783
    Epoch 3/10
    469/469 [==============================] - 54s 115ms/step - loss: 0.0507 - accuracy: 0.9844
    Epoch 4/10
    469/469 [==============================] - 55s 116ms/step - loss: 0.0389 - accuracy: 0.9879
    Epoch 5/10
    469/469 [==============================] - 54s 116ms/step - loss: 0.0314 - accuracy: 0.9907
    Epoch 6/10
    469/469 [==============================] - 57s 122ms/step - loss: 0.0290 - accuracy: 0.9912
    Epoch 7/10
    469/469 [==============================] - 54s 115ms/step - loss: 0.0252 - accuracy: 0.9918
    Epoch 8/10
    469/469 [==============================] - 57s 121ms/step - loss: 0.0217 - accuracy: 0.9931
    Epoch 9/10
    469/469 [==============================] - 63s 135ms/step - loss: 0.0180 - accuracy: 0.9939
    Epoch 10/10
    469/469 [==============================] - 55s 117ms/step - loss: 0.0172 - accuracy: 0.9942
<tensorflow.python.keras.callbacks.History at 0x7f77f57bbe80>

모델의 평가는 .evaluate()를 사용합니다.

model.evaluate(xte, yte, batch_size=batchSize, verbose=0)
[0.02676335722208023, 0.9915000200271606]

여기서 주요 변경 사항은 Conv2D 레이어의 사용입니다. ReLU 활성화 함수는 이미 Conv2D의 인수입니다. ReLU 기능은 Batch normalization layer가 모델에 포함될 때 Activation layer로 나올 수 있습니다. 배치 정규화는 훈련 중 불안정을 일으키지 않고 큰 학습률을 활용할 수 있도록 심층 CNN에서 사용됩니다.

댓글

이 블로그의 인기 게시물

[Linear Algebra] 유사변환(Similarity transformation)

유사변환(Similarity transformation) n×n 차원의 정방 행렬 A, B 그리고 가역 행렬 P 사이에 식 1의 관계가 성립하면 행렬 A와 B는 유사행렬(similarity matrix)이 되며 행렬 A를 가역행렬 P와 B로 분해하는 것을 유사 변환(similarity transformation) 이라고 합니다. $$\tag{1} A = PBP^{-1} \Leftrightarrow P^{-1}AP = B $$ 식 2는 식 1의 양변에 B의 고유값을 고려한 것입니다. \begin{align}\tag{식 2} B - \lambda I &= P^{-1}AP – \lambda P^{-1}P\\ &= P^{-1}(AP – \lambda P)\\ &= P^{-1}(A - \lambda I)P \end{align} 식 2의 행렬식은 식 3과 같이 정리됩니다. \begin{align} &\begin{aligned}\textsf{det}(B - \lambda I ) & = \textsf{det}(P^{-1}(AP – \lambda P))\\ &= \textsf{det}(P^{-1}) \textsf{det}((A – \lambda I)) \textsf{det}(P)\\ &= \textsf{det}(P^{-1}) \textsf{det}(P) \textsf{det}((A – \lambda I))\\ &= \textsf{det}(A – \lambda I)\end{aligned}\\ &\begin{aligned}\because \; \textsf{det}(P^{-1}) \textsf{det}(P) &= \textsf{det}(P^{-1}P)\\ &= \textsf{det}(I)\end{aligned}\end{align} 유사행렬의 특성 유사행렬인 두 정방행렬 A와 B는 'A ~ B' 와 같

[matplotlib] 히스토그램(Histogram)

히스토그램(Histogram) 히스토그램은 확률분포의 그래픽적인 표현이며 막대그래프의 종류입니다. 이 그래프가 확률분포와 관계가 있으므로 통계적 요소를 나타내기 위해 많이 사용됩니다. plt.hist(X, bins=10)함수를 사용합니다. x=np.random.randn(1000) plt.hist(x, 10) plt.show() 위 그래프의 y축은 각 구간에 해당하는 갯수이다. 빈도수 대신 확률밀도를 나타내기 위해서는 위 함수의 매개변수 normed=True로 조정하여 나타낼 수 있다. 또한 매개변수 bins의 인수를 숫자로 전달할 수 있지만 리스트 객체로 지정할 수 있다. 막대그래프의 경우와 마찬가지로 각 막대의 폭은 매개변수 width에 의해 조정된다. y=np.linspace(min(x)-1, max(x)+1, 10) y array([-4.48810153, -3.54351935, -2.59893717, -1.65435499, -0.70977282, 0.23480936, 1.17939154, 2.12397372, 3.0685559 , 4.01313807]) plt.hist(x, y, normed=True) plt.show()

R 미분과 적분

내용 expression 미분 2차 미분 mosaic를 사용한 미분 적분 미분과 적분 R에서의 미분과 적분 함수는 expression()함수에 의해 생성된 표현식을 대상으로 합니다. expression expression(문자, 또는 식) 이 표현식의 평가는 eval() 함수에 의해 실행됩니다. > ex1<-expression(1+0:9) > ex1 expression(1 + 0:9) > eval(ex1) [1] 1 2 3 4 5 6 7 8 9 10 > ex2<-expression(u, 2, u+0:9) > ex2 expression(u, 2, u + 0:9) > ex2[1] expression(u) > ex2[2] expression(2) > ex2[3] expression(u + 0:9) > u<-0.9 > eval(ex2[3]) [1] 0.9 1.9 2.9 3.9 4.9 5.9 6.9 7.9 8.9 9.9 미분 D(표현식, 미분 변수) 함수로 미분을 실행합니다. 이 함수의 표현식은 expression() 함수로 생성된 객체이며 미분 변수는 다음 식의 분모의 변수를 의미합니다. $$\frac{d}{d \text{변수}}\text{표현식}$$ 이 함수는 어떤 함수의 미분의 결과를 표현식으로 반환합니다. > D(expression(2*x^3), "x") 2 * (3 * x^2) > eq<-expression(log(x)) > eq expression(log(x)) > D(eq, "x") 1/x > eq2<-expression(a/(1+b*exp(-d*x))); eq2 expression(a/(1 + b * exp(-d * x))) > D(eq2, "x") a * (b * (exp(-d * x) * d))/(1 + b