안녕하세요, 여행벌입니다.

저번 포스팅에 이어서

"혈청 Micro-RNA 데이터와 기계학습을 통한 암종의 다중 분류"

준비 과정에 대해서 포스팅해보겠습니다.


[원본데이터 학습진행]

먼저, 원본데이터를 가지고 학습을 진행했습니다. 학습 과정은 다음과 같습니다.

7 : 3 의 비율로 Training Data와 Test Data를 나누고 다시 Training Data를 9 : 1의 비율로 Training Data와 Validation Data로 나눴습니다. 그 후, 학습률 0.01 로 500 epoch, 학습률을 0.001로 낮춰 500 epoch 총 1000 epoch 동안 학습을 진행했습니다. Output Layer는 log softmax를 활성화 함수로 사용하고, 나머지 layer에서는 ReLU 활성화 함수를 이용했습니다. 완전 연결 계층의 경우는 드랍아웃을 추가적으로 사용했고, 손실함수는 Cross entropy 함수를 이용했습니다. 최종적으로 평균 90.38%의 정확도를 얻었습니다.

# 데이터 나누기
train_data, test_data, train_target, test_target = train_test_split(data, target, test_size = 0.3)
train_data, validation_data, train_target, validation_target = train_test_split(train_data, train_target, test_size = 0.1)
#신경망구현
class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.fc1 = nn.Linear(2565,1280)
        self.fc2 = nn.Linear(1280,640)
        self.fc3 = nn.Linear(640,320)
        self.fc4 = nn.Linear(320,12)
        
    def forward(self, x):
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = F.relu(self.fc3(x))
        x = F.dropout(x, training=self.training)
        x = self.fc4(x)
        return F.log_softmax(x)

여러 번의 학습 중 하나의 예시에 대한 정답 테이블과 그래프입니다.

Training Data / Validataion Data 의 손실값에 대한 그래프입니다. (빨간색 : Validation 파란색 : Training Data)

Training Data / Validataion Data 의 정확도에 대한 그래프입니다. (빨간색 : Validation 파란색 : Training Data)

위의 그래프를 통해 학습이 제대로 이루어지지 않고 있는 것을 알 수 있고, 이는 비암환자 클래스가 압도적으로 많은 데이터 수를 가지고 있기 때문에 오버피팅 된 것으로 판단했습니다.

따라서, SMOTE 알고리즘을 통해 오버샘플링을 진행했고 각 클래스 별로 500개의 Data로 균일하게 크기를 맞춰 학습을 진행해봤습니다. 하지만 오히려 정확도가 60%까지 떨어지는 결과가 나왔고, 언더샘플링을 통해 크기가 100개 이상인 클래스를 모두 크기를 100으로 맞춰주고 학습을 진행한 경우에도 정확도가 7~ 80%까지 떨어지는 결과가 나왔습니다.

최종적으로 랜덤추출 오버샘플링을 통해 클래스 별로 Data 크기를 맞춰서 학습을 진행했습니다.

랜덤추출 오버샘플링 코드는 다음과 같습니다.

ninput = 301
dummy_data = pd.DataFrame()

for i in range(1,ninput):
    row = random.randrange(0,115)
    dummy_data = dummy_data.append(data1.iloc[row])
dummy_data.shape

data1은 유방암으로 원본 데이터는 115명에 대한 데이터입니다. 데이터를 300개로 키우기 위해 랜덤하게 데이터를 계속 추출합니다. 다음과 같은 과정을 모든 클래스에 대해서 진행하면 랜덤추출 오버샘플링을 통해 클래스 별로 Data 크기를 맞춘 데이터를 얻을 수 있습니다.

 

[시뮬레이션 결과]

각 암종별 데이터 수를 동일하게 300(G3), 500(G5), 700(G7), 1000(G10)으로 데이터를 각 2번 반복 생성하였습니다. 생성 방법은 무작위 추출을 통한 오버샘플링 기법을 활용하였습니다. 그 후 데이터별로 10번의 학습을 진행했습니다. 원본 데이터로 학습시킨 결과는 최대 91.65%, 평균 90.38% 정확도인데 반해서 오버샘플링 기법을 활용한 결과, 최대 99.50%의 정확도를 얻었습니다.

[결론]

제안된 모델은 9종류의 암종과 건강한 사람을 100%에 가까운 정확도로 구분합니다. 한 가지 우려되는 제한사항은 데이터에 대한 접근성으로 RNA 데이터가 세포 속에 있기 때문에 다른 데이터에 비해 비교적 채취하기가 어렵습니다. 하지만 microRNA가 암과 연관이 있음이 밝혀지고 이를 빠르고 효과적으로 얻기 위한 연구가 진행되고 있습니다. 현재는 microRNA가 혈청과 혈장을 타고 순환하는 사실이 밝혀지면서 혈중 microRNA 연구가 가속화되고 있습니다. 가까운 시일 내에 mircoRNA 데이터와 제안된 모델로 암을 진단하여 암의 오진율 및 사망률 감소를 기대합니다.


학습을 진행하는 과정은 밤새 연구실 서버에서 돌렸기 때문에 오히려 어렵지 않았습니다.

하지만 더 좋은 신경망을 찾기 위해서 층도 늘려보고 구조를 바꿔보고 하는 과정이 어려웠던 것 같습니다.

다음 포스팅에서는 학술 대회에서 교수님들께 받은 질문과 한계점에 대해서 포스팅해보도록 하겠습니다.

안녕하세요, 여행벌입니다.

이산수학 Express(2011) 4장 - 증명법 연습문제 풀이입니다.

틀린 부분은 같이 댓글로 얘기해보면

좋을 것 같습니다!

이산수학+Express+Chapter+4+문제풀이.pdf
2.69MB

안녕하세요, 여행벌입니다.

C언어로 쉽게 풀어 쓴 자료구조 3장 - 배열, 구조체, 포인터 연습문제 풀이입니다.

틀린 부분은 같이 댓글로 얘기해보면 좋을 것 같습니다!

자료구조3장.pdf
0.90MB

+) * 2번 답 1036번지로 수정하겠습니다!

문제 : https://www.acmicpc.net/problem/1655


[알고리즘풀이]

내림차순으로 정렬되는 Priority_Queue와 오름차순으로 정렬되는 Priority_Queue 2개를 이용해서 문제를 해결할 수 있습니다. 편의상 내림차순으로 정렬되는 Priority_Queue를 pq1 / 오름차순을 pq2 라고 하겠습니다.

pq1 은 중앙값보다 작거나 같은 수들을 담을 것이고, pq2는 중앙값보다 큰 수들을 담을 것입니다.

따라서, 위의 그림과 같은 상황이 됩니다.

 

입력이 들어오면 새로운 입력은 Mid보다 크거나 / 작거나 같습니다.

1) Mid보다 입력이 큰 경우.

입력이 Mid보다 크다면, 새로운 입력은 pq2에 Push되야합니다.

이때, pq2가 pq1보다 2개 이상 많아진다면 Mid에 영향을 주게 됩니다.

즉, Mid를 pq1에 Push해주고, pq2에서 가장 작은 값( pq2.top() )이 새로운 Mid가 됩니다.

예시를 통해 자세히 알아보겠습니다. 다음과 같은 상황이라고 해봅시다.

-2 -1 / 0 / 1 2 3

4가 새롭게 입력으로 들어온다면, -2 -1 0 / 1 / 2 3 4 로 Mid가 바뀌게 됩니다.

이 외에는 Mid에 변화가 없습니다.

 

2) Mid보다 작거나 같은 경우.

새로운 입력을 pq1에 Push되야합니다.

1)과 마찬가지로 이때, pq1이 pq2보다 커진다면 Mid에 영향을 주게 됩니다.

이번엔 반대로 Mid가 작아져야 합니다. 따라서, 현재 Mid를 pq2에 push해주고, pq1에서 새로운 Mid를 가져옵니다.

예시를 통해 자세히 알아보겠습니다. 다음과 같은 상황이라고 해봅시다.

-2 -1 0 / 1 / 2 3 4

-4가 새롭게 입력으로 들어온다면 -4 -2 -1 / 0 / 1 2 3 4 로 pq1에서 새로운 Mid를 가져와야 합니다.

이 외에는 Mid에 변화가 없습니다.

 

우선순위 큐를 위와 같이 2개를 이용하면 문제가 간단하지만, 이 아이디어를 생각하기까지 시간이 오래 걸렸던 문제입니다.

#include<iostream>
#include<queue>

using namespace std;

int main(void) {
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);

	int n, mid, x;
	priority_queue<int> pq1;
	priority_queue<int, vector<int>, greater<int>> pq2;

	cin >> n >> mid;
	cout << mid << '\n';
	for (int i = 1; i < n; i++) {
		cin >> x;
		if (x > mid) {
			pq2.push(x);
			if (pq1.size() == pq2.size() - 2) {
				pq1.push(mid);
				mid = pq2.top();
				pq2.pop();
			}
		}
		else {
			pq1.push(x);
			if (pq1.size() > pq2.size()) {
				pq2.push(mid);
				mid = pq1.top();
				pq1.pop();
			}
		}
		cout << mid << '\n';
	}
}

 

+ Recent posts