기본 콘텐츠로 건너뛰기

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

선형신경망_linear Regression

목차

  1. 선형신경망
    1. Minibatch Stocastic Gradient Descent
    2. 정규분포와 제곱손실
    3. Minibatch의 생성
    4. 모델 생성
    5. high-level APIs of deep learning frameworks

선형신경망

회귀분석은 하나이상의 독립변수와 종속변수 사이의 관계를 모형화는 일련의 방법입니다. 예측, 분류와 관계된 경우에 사용합니다.
예로 가격 예측(주택, 주식 등), 입원 기간 예측(병원 환자의 경우), 수요 예측(소매 판매) 등을 생각할 수 있습니다.

선형회귀의 경우 다음을 가정합니다.

  • 독립 변수 x와 종속 변수 y 사이의 관계가 선형이라고 가정합니다. 즉, 관측치에 대한 약간의 노이즈가 주어졌을 때 y는 x에 있는 요소의 가중 합으로 표현될 수 있다.
  • 이 모델에서 파생되는 노이즈는 정규분포에 부합한다고 가정합니다.

가격, 면적, 연령으로 구성된 자료에서 가격을 예측하기 위한 선형모델을 구축한다고 할 때 그 데이터 셋은 다음과 같은 모양일 것입니다.

면적 연령 가격
- - -
$\vdots$ $\vdots$ $\vdots$

위 데이터 구조에서

  • 각 행을 예제(데이터 포인트, 데이터 인스턴스, 샘플)이라 합니다.
  • 예측하고자 하는 것을 레벨(타겟)이라 합니다.
  • 에측의 기반이 되는 독립변수를 특성(feature) 또는 공변량(covariate)라고 합니다.
  • 데이터셋의 전체 수(행의수)를 n, 데이터 인스턴스(예제)를 i로 표현합니다. 그러므로 입력변수 X와 y는 다음과 같습니다. $$X^{(i)}=[x^{(i)}_1, x^{(i)}_2]^T, y^{(i)}$$
$$\begin{align}\tag{1}&\text{price}=w_{area} \cdot \text{area}+w_{age} \cdot \text{age}+b\\ &w_{area}, w_{age}: \text{area, age의 가중치}\\ & b:\text{bias}\end{align}$$

위 식은 독립변수들과 각각에 대응하는 가중치를 고려한 결과와 편차를 구성요소로 하는 아핀변환입니다. 이 변환의 결과는 하나이상의 특징(들)로 부터 예측된 값으로 $\hat{y}$로 나타냅니다. 이 모델은 라벨의 값과 예측값이 같아지도록 가중치와 편차를 조정하는 과정의 결과입니다.

$$\begin{align}\tag{2}\hat{y} = w_1x_1+ \cdots + w_d x_d+b\end{align}$$

여러 특징들이 존재하는 경우 각 인스턴스를 벡터로 구성하여 다음과 같이 선형대수적으로 계산할 수 있습니다.

$$\begin{align}\tag{3} \hat{y}&=\mathbf{w}^T\mathbf{x}+b \\\mathbf{x}& \in \mathbb{R}^d\\\mathbf{w}& \in \mathbb{R}^d \end{align}$$

모든 인스턴스를 대상으로 하면 위 식 3은 다음과 같이 수정됩니다.

$$\begin{align}\tag{4} \hat{y}=\mathbf{X}\mathbf{w}^T+b\end{align}$$

식 4의 모델에서 매개변수인 w,b를 찾는 과정은 손실(loss) 즉, 예측값과 실제값의 차이를 감소시키는 과정과 같습니다.이것은 다음의 과정으로 이루어집니다.

  • 실제값과 예측값의 차이(손실)의 계량화
  • 계량된 값을 최소화하기 위한 매개변수의 업데이트

손실함수는 실제값과 예측값 사이의 거리를 정량화하는 할 수 있어야 합니다. 각 인스턴스에서 산출된 손실이 음수와 양수 모든 값을 갖는다면 손실의 총합은 실제보다 작아지는 문제를 가집니다. 그러므로 손실은 음수가 아니어야 합니다. 이에 적합한 척도로 제곱오차를 사용합니다. 일반적으로 손실함수(l(i))식 5와 같습니다.

$$\begin{align}\tag{5} l^{(i)}(w, b)=\frac{1}{2}\left(\hat{y}^{(i)}-y^{(i)} \right)^2\end{align}$$

식 5에서 $\displaystyle \frac{1}{2}$는 위함수의 도함수를 적용할 경우 계산상의 편의를 위해 사용한 것입니다.

다음 그림은 회귀모델에 의한 예측치와 실제값사이의 차이를 나타낸 것입니다.

np.random.seed(1)
x=np.arange(1, 6)
y=x+2*np.random.rand(5)
y2=x+0.1
plt.scatter(x, y, label="real")
plt.scatter(x, y2, label="predict", color="black")
plt.plot(x, y2, color="black")
plt.vlines(2, y2[1], y[1], color="red", linestyle='solid')
plt.text(2, y[1]+0.2, 'y', size=12, weight="bold", color="blue")
plt.text(2, y2[1]-0.3, r'$\mathbf{\hat{y}}$', size=12 ,color="black")
plt.xticks([])
plt.yticks([])
plt.legend(loc='best')
plt.show()

전체 데이터의 손실을 계산은 각 인스턴스의 손실들의 평균으로 계산합니다.

$$\begin{align}\tag{6} L(w, b)&=\frac{1}{n}\sum^n_{i=1}l^{(i)}(w, b)\\&=\frac{1}{n}\sum^n_{i=1}\frac{1}{2}\left(w^T x^{(i)}-y^{(i)} \right)^2\end{align}$$

식 6의 결과가 최소가 되는 지점의 매개변수(w*,b*)를 발견합니다.

$$\begin{align}\tag{7} w^*, b^*=\underset{w, b}{\text{argmin}}\; L(w, b)\end{align}$$

편차와 가중치 벡터를 행렬로 전환하여 식 8을 사용하는 분석적 방법에 의해 식 7에 만족 즉, 손실을 최소화하는 가중치 행렬을 계산할 수 있습니다.

$$\begin{align}\tag{8} w^*=(X^T X)^{-1}X^Ty\end{align}$$

식 8은 식 6의 2차함수로 표현되는 손실 함수의 임계점이 하나만 존재하는 경우에 최소값에 해당하며 적용할 수 있습니다. 즉, 선형 회귀는 전체 데이터에 대해 최소값이 하나만 있는 학습 문제입니다. 그러나 심층 네트워크와 같은 더 복잡한 모델의 경우 손실 표면에는 많은 최소값이 포함될 수 있으므로 실제 데이터에서 그 조건들의 부합여부는 제한적입니다. 그러므로 딥러닝의 경우 식 8을 적용하지 않습니다.

Minibatch Stocastic Gradient Descent

딥러닝에서는 분석적 방법 대신에 손실함수를 점차적으로 감소시키는 방향으로 매개변수를 업데이트하는 과정을 반복합니다. 이 알고리즘을 경사하강법(gradient descent)라고 합니다. 이 과정은 손실을 가중치에 관여된 변수를 기준으로 도함수를 계산하여 기존의 가중치에 감하여 새로운 가중치를 계산하는 것이 핵심입니다.

for i in range(n):
 $\hat{y}=wX$
 Loss=$\sum(y-\hat{y})^2$
 $w'=\frac{dL}{dX}$
 w=w-ζw'

위의 의사코드에서 ζ는 가중치(w)의 변화 폭을 조정하기 위한 계수로서 학습률(learning rate)라고 하며 X는 데이터의 모든 변수들의 인스턴스를 의미합니다. 데이터 셋이 매우 클 경우 인스턴스들을 일정한 크기로 구분하여 소그룹별로 위 과정을 실행합니다. 이러한 과정을 미니배치(minibatch)라 합니다.

for i in range(n):
 for j in minibatch:
   $\hat{y}=wX$
   Loss=$\sum(y-\hat{y})^2$
   $w'=\frac{dL}{dX}$
   w=w-w'

배치크기와 학습률은 수동으로 미리 지정되어 계산의 반복과정 즉, 학습을 통해 생성되지 않습니다. 이러한 매개변수를 초매개변수(hyperparameter)라고 합니다. 이 초메개변수는 학습과정에서 조정되지 않으므로 학습에 참여하지 않은 데이터(검증데이터)에 적용하여 평가합니다. 이 평가에 의해 조절될 수 있습니다.

모델을 훈련할 때 일반적으로 예제의 전체 미니 배치를 동시에 처리하기를 원합니다. 이를 효율적으로 수행하려면 Python에서 값비싼 for 루프를 작성하는 대신 계산을 벡터화하고 빠른 선형 대수 라이브러리를 활용해야 합니다.

정규분포와 제곱손실

제곱손실을 정규분포에 적용하여 봅니다. 다음은 정규분포의 확률밀도 함수입니다.

$$\tag{9}p(x)=\frac{1}{\sqrt{2 \pi \sigma^2}}\exp\left(-\frac{1}{2 \sigma^2}(x-\mu)^2\right)$$
def normal(x, mu, sigma):
    p=1/(np.sqrt(2*np.pi*sigma**2))
    return p*np.exp(-0.5*1/sigma**2*(x-mu)**2)
x=np.arange(-7, 7, 0.01)
param=[(0,1),(0,2),(3,1)]
y={}
for i, j in param:
    plt.plot(x, normal(x, i, j), label=f"mean:{i}, std:{j}")
plt.xlabel("x")
plt.ylabel("p(x)")
plt.legend(loc="best")
plt.show()

위 결과와 같이 평균의 변경은 x축에 관계되며 분산은 분포의 피크와 관계됩니다. 산형회귀의 평균제곱오차(손실)은 이 정규분포를 따른다는 가정을 만족하여야 합니다.

$$\tag{10}y=w^Tx+b+\epsilon, \quad \epsilon \sim \text{N}(0, \sigma^2)$$

위 식의 ε은 정규분포를 따르기 때문에 입력된 x에 대응하는 y의 우도(likelihood)를 계산할 수 있습니다.

$$\tag{11}P(y|x)=\frac{1}{\sqrt{2 \pi \sigma^2}}\exp\left(-\frac{1}{2 \sigma^2}\left(y-w^Tx-b\right)^2\right)$$

최대우도(maximum likelihood)에 따라 매개변수 w,b 의 최적값은 식 12와 같이 계산되는 모든 데이터의 우도를 최대화시킵니다.

$$\tag{12}P(y|X)=\underset{i=1}{\overset{n}{\Pi}}\;p(y^{(i)}|x^{(i)})$$

식 11은 지수화의 결과로서 값의 스케일이 매우커질 수 있습니다. 데이터 분석에서 수치를 작고 일정한 규모로 만드는 것이 유리합니다. 그러므로 식 13과 같이 로그화하여 적용합니다 또한 일반적으로 최적화는 최대화보다는 최소화에서 이루어지므로 음의 로그 우도(negative log-likelihood)를 사용합니다.

$$\tag{13}-\log P(y|x)=\sum^n_{i=1}\left(\frac{1}{2}\log (2 \pi \sigma^2) +-\frac{1}{2 \sigma^2}\left(y-w^Tx^{(i)}-b\right)^2 \right)$$

선형회귀와 신경망을 위한 인공 자료를 생성하기 위해 함수를 정의합니다.

def syntheticData(w, b, num):
    X=torch.normal(0, 1, (num, len(w)))
    y=torch.matmul(X, w)+b
    y += torch.normal(0, 0.01, y.shape)
    return X,y.reshape((-1, 1))
wTrue=torch.tensor([2, -3.4])
bTrue=4.2
feature, labels=syntheticData(wTrue, bTrue, 1000)
feature[:3]
tensor([[-0.4112,  0.5959],
        [-0.8951,  1.3652],
        [ 2.2625, -0.9346]])
labels[:3]
tensor([[ 1.3352],
        [-2.2343],
        [11.9169]])
plt.figure(figsize=(10, 5))
plt.scatter(feature[:,1].detach().numpy(), labels)
plt.show()

Minibatch의 생성

미니배치를 생성하기 위한 함수를 정의합니다.

def dataIter(batchSize, features, labels):
    num=len(features)
    idx=list(range(num))
    np.random.shuffle(idx)
    for i in range(0, num, batchSize):
        batchIdx=torch.tensor(idx[i:min(batchSize, num)])
        yield features[batchIdx], labels[batchIdx]
batchSize=10
for X, y in dataIter(batchSize, feature, labels):
    print(X, '\n', y)
    break
tensor([[-0.8741, -0.3601],
           ⁞        ⁝,
        [-0.6073,  0.2934]]) 
 tensor([[ 3.6662],
           ⁞,
        [ 1.9897]])
#원 데이터
feature[:10]
tensor([[-0.4112,  0.5959],
        [-0.8951,  1.3652],
             ⁞      ⁞,
        [-0.9596,  0.6170]])

위에서 정의한 함수의 경우 메모리에 모든 데이터를 로드하고 많은 랜덤 메모리 엑서스를 수행해야 하므로 데이터의 크기가 큰 경우 비효율적일 수 있습니다. 대안으로 다음의 pytorch의 두 클래스를 사용할 수 있습니다.

from torch.utils.data import TensorDataset, DataLoader
dataSet=TensorDataset(feature, label)
dataLoader=TensorDataLoader(dataSet, batch_size=batchSize, shuffle=True)

from torch.utils.data import TensorDataset, DataLoader
dataSet=TensorDataset(feature, labels)
dataLoader=DataLoader(dataSet, batchSize, shuffle=True)
for X,y in dataLoader:
    print(X, '\n', y)
    break
tensor([[ 0.2103, -3.1670],
        [ 0.2690, -0.0366],
             ⁞     ⁞,
        [ 0.5179, -0.1962],
        [-1.5954, -1.7739]]) 
 tensor([[15.3691],
        [ 4.8877],
           ⁞,
        [ 5.9061],
        [ 7.0558]])

모델 생성

변수의 초기화

모델을 생성하기 위해 시작할 가중치와 편차의 매개변수가 필요합니다. 초기값으로 0을 지정할 수 있지만 일반적으로 random 값을 사용합니다. 다음은 가중치를 평균=0, 표준편차=0.1의 정규분포의 랜덤값과 편차 0을 사용합니다.

w=torch.normal(0, 0.01, size=(2, 1), requires_grad=True)
b=torch.zeros(1, requires_grad=True)
w, b
(tensor([[-0.0111],
         [-0.0033]], requires_grad=True),
 tensor([0.], requires_grad=True))

위의 가중치와 편차를 입력에 연결하는 모델을 정의합니다. 선형모델은 다음에 정의하는 함수와 같이 입력(X)와 가중치(w) 두 벡터의 내적에 편차를 첨가하는 것으로 모델을 설정할 수 있습니다.

def linearReg(X, w, b):
    return(torch.matmul(X, w)+b)

위 모델의 초기화된 가중치와 편차의 업데이트를 위해서 손실을 계산하고 그 손실에 대한 도함수를 사용합니다. 손실은 다음의 정의된 함수에 의해 걔산할 수 있습니다.

def squaredLoss(yhat, y):
    return((yhat-y)**2/2)

위에서 정의된 함수의 결과인 손실의 미분으로 가중치와 편차를 업그레이드 할 수 있습니다. 미분은 .backward() 메소드를 사용합니다. 이 메소드의 결과는 각 매개변수의 기울기(parameter.grad)를 반환합니다. 그 결과로 부터 다음 정의된 함수를 사용하여 가중치와 편차를 업그레이드를 합니다. 업그레드 시 미분은 정지해야 하므로 torch.no_grad()를 지정합니다.

def sgd(param, lr, batchSize):
    with torch.no_grad():
        for par in param:
            par -= lr*par.grad/batchSize
            par.grad.zero_()

위에서 정의한 함수들을 사용하여 데이터의 학습을 진행합니다.

lr=0.03
num=3
model=linearReg
loss=squaredLoss
for epoch in range(num):
    for x, y in dataLoader:
        l=loss(model(x, w, b), y)
        l.sum().backward()
        sgd([w, b], lr, batchSize)
    with torch.no_grad():
        trainLoss=loss(model(feature, w, b), labels)
        print(f'Epoch:{epoch}, loss:{np.round(float(trainLoss.mean()),5)}')
Epoch:0, loss:0.03144
 Epoch:1, loss:0.00011
 Epoch:2, loss:6e-05
print(f'w의 오차:{wTrue-w.reshape(wTrue.shape)}')
print(f'b의 오차:{bTrue-b}')
w의 오차:tensor([ 3.6240e-05, -8.3876e-04], grad_fn=>SubBackward0<)
 b의 오차:tensor([0.0005], grad_fn=>RsubBackward1<)

high-level APIs of deep learning frameworks

위에서 모델을 구현하는 함수는 pytorch.nn의 Sequenctial() 클래스와 nn.Linear()등의 여러 선형과 비선형함수를 결합하여 대체할 수 있습니다. Sequenctial 클래스로 하나 이상의 여러 함수를 연결할 수 있습니다.

model=Sequential(nn.Linear(입력차원, 출력차원), #첫번째 층
                 nn.ReLU(), #첫번째 층의 비활성
                 …,
                 nn.Linear() #최종 출력층)

위의 예는 선형모델로서 하나의 층(레이어)으로 구성되어 있으므로 즉, 단일 계층 네트워크의 아키텍처이므로 실제로 Sequential이 필요하지 않습니다. 이 단일 층은 각 입력이 행렬-벡터 곱셈을 통해 각 출력에 연결되어 있으므로 완전 연결(fully connected)이라고 합니다. PyTorch에서 완전 연결 계층은 Linear 클래스에 정의됩니다. nn.Linear은 입력의 features의 수(열 차원)과 최종 출력 차원을 인수로 갖습니다. 이 경우는 2개의 입력차원과 1개의 출력차원을 지정합니다.

import torch.nn as nn
net=nn.Sequential(nn.Linear(2, 1))

nn.Linear()함수는 가중치와 편차를 무작위로 생성합니다.

net[0].weight.data, net[0].bias.data
(tensor([[ 0.2108, -0.0792]]), tensor([0.6340]))

각 가중치와 편차에 값을 전달함으로서 의도적으로 초기값을 지정할 수 있습니다.

net[0].weight.data.normal_(0, 0.01)
tensor([[-0.0052,  0.0097]])
net[0].bias.data.fill_(0)
tensor([0.])

모델 net의 매개변수(가중치와 편차)는 parameters() 메소드에 저장됩니다.

for i in net.parameters():
    print(i)
Parameter containing:
tensor([[-0.0052,  0.0097]], requires_grad=True)
Parameter containing:
tensor([0.], requires_grad=True)

위에서 정의한 squareLoss()는 squared L2 Norm 함수인 nn.MSELoss()함수로 대체할 수 있습니다.

loss=nn.MSELoss()

pytorch는 다양한 최적화 알고리즘에 관한 함수를 포함하고 있습니다. 이것은 위에서 정의한 sgd()와 같이 매개변수를 업데이트하는 목적으로 사용됩니다. 이 예에서 확률적 경사 하강법(SGD)를 적용하므로 torch.optim.SGD(매개변수, 학습률) 함수를 사용합니다. 매개변수의 값들은 위에서 나타낸 것과 같이 model.parameter() 메서드로 연결할 수 있으므로 학습률만 전달합니다.

opt=torch.optim.SGD(net.parameters(), lr=0.03)

위 함수를 통과하여 업데이트를 위한 결과 즉, $\zeta \cdot \frac{dL}{dw}$을 반환합니다. (ζ: 학습률) 이 결과를 기반으로 매개변수의 업데이트는 .step() 메서드에 의해 이루어집니다. 또한 opt의 결과인 매개변수의 기울기는 gradient가 가능한 값이므로 prarm.grad 형식으로 그 매개변수에 도함수가 계속진행된다면 모든 값들이 축적됩니다. 그러므로 이 결과이후에 축적된 기울기를 0으로 만들어야 하며 .zero_grad()메소드를 사용하여 성취할 수 있습니다.이러한 함수와 메소드를 사용하여 학습은 다음 과정은 다음과 같으며 위에서 진행한 과정에 비해 심플한 코드로 구성됩니다.

for epoch in range(num):
    for x, y in dataLoader:
        l=loss(net(x), y)
        opt.zero_grad()
        l.backward()
        opt.step()
    trainLoss=loss(net(feature), labels)
    print(f'Epoch:{epoch}, loss:{np.round(float(trainLoss),5)}') 
Epoch:0, loss:0.00039
Epoch:1, loss:0.00011
Epoch:2, loss:0.00011

최종 구현된 모델의 가중치와 편차와 실제 가중치와 편차의 오차를 나타내봅니다.

w, b=list(net.parameters())  #=net[0].weight.data, net[0].bias.data
w.data, b.data
(tensor([[ 1.9993, -3.4003]]), tensor([4.2003]))
print(f'w의 오차:{wTrue-w.reshape(wTrue.shape)}')
print(f'b의 오차:{bTrue-b}')
w의 오차:tensor([0.0007, 0.0003], grad_fn=>SubBackward0<)
b의 오차:tensor([-0.0003], grad_fn=>RsubBackward1<)

댓글

이 블로그의 인기 게시물

[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