728x90
반응형

728x90

안녕하세요! 오늘은 gymnasium에서 제공하는 cart pole 문제 설명에 대한 포스팅 진행하겠습니다.

 

저는 cart pole 문제를 DQNDDQN 두 가지 방법으로 풀어보았는데요!

각각 전체 코드는 포스팅 가장 아래에 있습니다.


  • cart pole 규칙

우선 gymnasium에서 제공하는 cart pole 문제는 아래 링크에서 자세하게 보실 수 있습니다.

https://gymnasium.farama.org/environments/classic_control/cart_pole/

 

Gymnasium Documentation

A standard API for reinforcement learning and a diverse set of reference environments (formerly Gym)

gymnasium.farama.org

 

위의 사진처럼 생긴 막대를 지속적으로 세우는 것이 cart pole의 가장 큰 목표 입니다.

cart pole의 자세한 규칙은 아래와 같습니다.

  1. 이동 방향좌, 우만 존재
  2. 현재 막대의 상태는 막대 위치, 속도, 각도, 각속도 총 4가지로 표현
  3. 하나의 step이 에피소드 종료되지 않으면 보상 1을 획득
  4. 각도가 좌우 12 º 이상 벗어나거나, 막대가 화면을 벗어나거나, 보상 500(혹은 200) 달성하면 에피소드 종료

저희는 위의 규칙을 잘 생각하여, 막대가 특정 각도를 넘어가거나 화면에 벗어나지 않게 500 에피소드 이상 버티는 것을 학습해주어야 합니다.

  • 필요한 라이브러리 설치

frozen lake는 gymnasium에서 제공하고 있기 때문에 가장 먼저 gymnasium을 설치해주시면 됩니다!

그리고 render 모드 활성화를 진행하기 위해 gymnasium[toy-text] 설치도 같이 진행해주세요.

두 패키지 모두 pip install로 설치해주시면 금방 설치할 수 있습니다.

import gymnasium as gym
from collections import defaultdict
import numpy as np
  • cart pole 환경 세팅

필요한 라이브러리를 설치한 후에는 cart pole 환경을 세팅해봅니다.

우선 환경 세팅을 하는 코드는 아래와 같습니다.

env = gym.make("CartPole-v1")

cart pole은 학습을 진행해주지 않으면 바로 에피소드가 종료가 되기 때문에 테스트를 진행해보기가 어렵습니다ㅠㅠ

어떤 식으로 진행되는지 궁금하신 분들은 전체 코드 복사 후 실행해보시면, cart pole이 어떻게 진행되는지 아실 수 있습니다!


여기까지가 cart pole 환경 세팅이었습니다.

cart pole 환경 세팅은 그렇게 어렵지 않아서, 금방 하실 수 있어요!

 

이번 포스팅에서는 DQN과 DDQN에 관한 전체 코드만 업로드하고, 자세한 포스팅은 추후에 진행하도록 하겠습니다.

두 가지 모두 500 에피소드에 달성한 모델이 다수 존재했습니다.

[DQN 전체 코드]

import gymnasium as gym
import torch
import torch.nn as nn
import torch.optim as optim
import random
import numpy as np
from collections import deque
import matplotlib.pyplot as plt
import os
import glob
import torch.nn.functional as F

# 하이퍼 파라미터
gamma = 0.99
learning_rate = 0.001
batch_size = 100
memory_size = 5000
# epsilon에 의해 행동을 선택할 때는 해당 부분 필요
# epsilon_start = 1.0
# epsilon_end = 0.001
# epsilon_decay = 0.995
episodes = 5000

class DQN(nn.Module):
    def __init__(self, state_dim, action_dim):
        super(DQN, self).__init__()
        self.fc1 = nn.Linear(state_dim, 64)
        self.fc2 = nn.Linear(64, 64)
        self.fc3 = nn.Linear(64, action_dim)

    def forward(self, x):
    	x = torch.relu(self.fc1(x))
        x = torch.relu(self.fc2(x))
        x = self.fc3(x)
        return x

class ReplayMemory:
    def __init__(self, max_len):
        self.memory = deque(maxlen=max_len)

    def push(self, transition):
        self.memory.append(transition)

    def sample(self, batch_size):
        return random.sample(self.memory, batch_size)

    def __len__(self):
        return len(self.memory)

def select_action(state, target_net, action_dim):
	# epsilon-greedy를 바탕으로 행동을 선택하는 과정
    # if random.random() < epsilon:
    #     return random.randint(0, action_dim - 1)
    # else:
    #     return target_net(state).argmax().item()
    
    # 행동 값을 확률로 변경하여, 확률에 따라 행동을 선택하는 과정
    q_value = target_net(state)
    p = F.softmax(q_value, dim=0).tolist()
    p = np.array(p)
    p /= p.sum()
    action = np.random.choice(action_dim, p=p)
    return action

def optimize_model(memory, policy_net, target_net, optimizer):
    if len(memory) < batch_size:
        return

    transitions = memory.sample(batch_size)
    batch = list(zip(*transitions))
    
    state_batch = torch.stack(batch[0])
    action_batch = torch.tensor(batch[1]).unsqueeze(1)
    reward_batch = torch.tensor(batch[2])
    next_state_batch = torch.stack(batch[3])
    done_batch = torch.tensor(batch[4], dtype=torch.float32)
    
    # DQN
    q_values = policy_net(state_batch).gather(1, action_batch)
    next_q_values = target_net(next_state_batch).max(1)[0].detach()
    target_q_values = reward_batch + (gamma * next_q_values * (1 - done_batch))
    loss = nn.MSELoss()(q_values.squeeze(), target_q_values)
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()


env = gym.make("CartPole-v1")
state_dim = env.observation_space.shape[0]
action_dim = env.action_space.n

policy_net = DQN(state_dim, action_dim)
target_net = DQN(state_dim, action_dim)

target_net.load_state_dict(policy_net.state_dict())
target_net.eval()

optimizer = optim.Adam(policy_net.parameters(), lr=learning_rate)
memory = ReplayMemory(memory_size)

# epsilon-greedy 방법으로 행동을 선택할 때 필요
# epsilon = epsilon_start

episode_rewards = []
episode_reward = 0

save_dir = "dqn_saved_models"
os.makedirs(save_dir, exist_ok=True)

# 500을 10회 진행하면 성공이라고 판단하여, 종료를 하기 위한 장치로 잠깐 쓰인 것
# count = 0

for episode in range(episodes):
    # if count > 10 :
    #     break
    state = torch.tensor(env.reset()[0], dtype=torch.float32)
    if episode % 1000 == 0: 
        print(f"Episode {episode}, Avg Reward: {episode_reward/1000}")
    if episode % 1000 == 0 :
        episode_reward = 0
    total_reward = 0

    # 500 초과인 경우는 done으로 판단
    while total_reward < 501 :
        action = select_action(state, target_net, action_dim)
        next_state, reward, done, _, _ = env.step(action)
        next_state = torch.tensor(next_state, dtype=torch.float32)
        memory.push((state, action, reward, next_state, done))

        state = next_state
        total_reward += reward

        optimize_model(memory, policy_net, target_net, optimizer)
        
        if done :
            break
    # 500점 달성한 모델 저장
    if total_reward >= 500 :
    #    count += 1
         model_path = os.path.join(save_dir, f"dqn_model_episode_{episode}.pth")
         torch.save(policy_net.state_dict(), model_path)
    
    episode_reward += total_reward
    # epsilon-greedy로 action을 선택할 때는 있어야 함
    # if episode % 10 == 0 :
    #     epsilon = max(epsilon_end, epsilon*epsilon_decay)
    
    if episode % 20 == 0:
        target_net.load_state_dict(policy_net.state_dict())

    episode_rewards.append(total_reward)

plt.plot(episode_rewards)
plt.xlabel('Episode')
plt.ylabel('Total Reward')
plt.title('DQN on CartPole')
plt.show()

# 테스트 진행 - render를 켜줘야 확인이 가능
env = gym.make("CartPole-v1", render_mode='human')
# 500 달성한 모델 업로드
model_paths = glob.glob(os.path.join(save_dir, "*.pth"))

model_i = 0
for model_path in model_paths :
    policy_net.load_state_dict(torch.load(model_path))
    policy_net.eval()
    
    avg_reward = 0
    
    # 각 모델 별 10번 진행
    for episode in range(10) :    
        state = torch.tensor(env.reset()[0], dtype=torch.float32)
        total_reward = 0

        while total_reward < 501 :
            with torch.no_grad() :
                action = policy_net(state).argmax().item()

            next_state, reward, done, _, _ = env.step(action)
            next_state = torch.tensor(next_state, dtype=torch.float32)

            state = next_state
            total_reward += reward
            
            if done :
                break

        avg_reward += total_reward
    print(f"model {model_i + 1}, Avg Reward: {avg_reward/10}")
    model_i += 1

[DDQN 전체 코드]

import gymnasium as gym
import torch
import torch.nn as nn
import torch.optim as optim
import random
import numpy as np
from collections import deque
import matplotlib.pyplot as plt
import os
import glob
import torch.nn.functional as F

# 하이퍼 파라미터
gamma = 0.99
learning_rate = 0.001
batch_size = 100
memory_size = 5000
# epsilon에 의해 행동을 선택할 때는 해당 부분 필요
# epsilon_start = 1.0
# epsilon_end = 0.001
# epsilon_decay = 0.995
episodes = 5000

class DQN(nn.Module):
    def __init__(self, state_dim, action_dim):
        super(DQN, self).__init__()
        self.fc1 = nn.Linear(state_dim, 64)
        self.fc2 = nn.Linear(64, 64)
        self.fc3 = nn.Linear(64, action_dim)

    def forward(self, x):
    	x = torch.relu(self.fc1(x))
        x = torch.relu(self.fc2(x))
        x = self.fc3(x)
        return x

class ReplayMemory:
    def __init__(self, max_len):
        self.memory = deque(maxlen=max_len)

    def push(self, transition):
        self.memory.append(transition)

    def sample(self, batch_size):
        return random.sample(self.memory, batch_size)

    def __len__(self):
        return len(self.memory)

def select_action(state, target_net, action_dim):
	# epsilon-greedy를 바탕으로 행동을 선택하는 과정
    # if random.random() < epsilon:
    #     return random.randint(0, action_dim - 1)
    # else:
    #     return target_net(state).argmax().item()
    
    # 행동 값을 확률로 변경하여, 확률에 따라 행동을 선택하는 과정
    q_value = target_net(state)
    p = F.softmax(q_value, dim=0).tolist()
    p = np.array(p)
    p /= p.sum()
    action = np.random.choice(action_dim, p=p)
    return action

def optimize_model(memory, policy_net, target_net, optimizer):
    if len(memory) < batch_size:
        return

    transitions = memory.sample(batch_size)
    batch = list(zip(*transitions))
    
    state_batch = torch.stack(batch[0])
    action_batch = torch.tensor(batch[1]).unsqueeze(1)
    reward_batch = torch.tensor(batch[2])
    next_state_batch = torch.stack(batch[3])
    done_batch = torch.tensor(batch[4], dtype=torch.float32)
    
    # DDQN
    q_values = policy_net(state_batch).gather(1, action_batch)
    next_action = policy_net(next_state_batch).argmax(1).unsqueeze(1)
    next_q_values = target_net(next_state_batch).gather(1, next_action).squeeze().detach()
    target_q_values = reward_batch + (gamma * next_q_values * (1 - done_batch))
    loss = nn.MSELoss()(q_values.squeeze(), target_q_values)
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()


env = gym.make("CartPole-v1")
state_dim = env.observation_space.shape[0]
action_dim = env.action_space.n

policy_net = DQN(state_dim, action_dim)
target_net = DQN(state_dim, action_dim)

target_net.load_state_dict(policy_net.state_dict())
target_net.eval()

optimizer = optim.Adam(policy_net.parameters(), lr=learning_rate)
memory = ReplayMemory(memory_size)

# epsilon-greedy 방법으로 행동을 선택할 때 필요
# epsilon = epsilon_start

episode_rewards = []
episode_reward = 0

save_dir = "dqn_saved_models"
os.makedirs(save_dir, exist_ok=True)

# 500을 10회 진행하면 성공이라고 판단하여, 종료를 하기 위한 장치로 잠깐 쓰인 것
# count = 0

for episode in range(episodes):
    # if count > 10 :
    #     break
    state = torch.tensor(env.reset()[0], dtype=torch.float32)
    if episode % 1000 == 0: 
        print(f"Episode {episode}, Avg Reward: {episode_reward/1000}")
    if episode % 1000 == 0 :
        episode_reward = 0
    total_reward = 0

    # 500 초과인 경우는 done으로 판단
    while total_reward < 501 :
        action = select_action(state, target_net, action_dim)
        next_state, reward, done, _, _ = env.step(action)
        next_state = torch.tensor(next_state, dtype=torch.float32)
        memory.push((state, action, reward, next_state, done))

        state = next_state
        total_reward += reward

        optimize_model(memory, policy_net, target_net, optimizer)
        
        if done :
            break
    # 500점 달성한 모델 저장
    if total_reward >= 500 :
    #    count += 1
         model_path = os.path.join(save_dir, f"dqn_model_episode_{episode}.pth")
         torch.save(policy_net.state_dict(), model_path)
    
    episode_reward += total_reward
    # epsilon-greedy로 action을 선택할 때는 있어야 함
    # if episode % 10 == 0 :
    #     epsilon = max(epsilon_end, epsilon*epsilon_decay)
    
    if episode % 20 == 0:
        target_net.load_state_dict(policy_net.state_dict())

    episode_rewards.append(total_reward)

plt.plot(episode_rewards)
plt.xlabel('Episode')
plt.ylabel('Total Reward')
plt.title('DDQN on CartPole')
plt.show()

# 테스트 진행 - render를 켜줘야 확인이 가능
env = gym.make("CartPole-v1", render_mode='human')
# 500 달성한 모델 업로드
model_paths = glob.glob(os.path.join(save_dir, "*.pth"))

model_i = 0
for model_path in model_paths :
    policy_net.load_state_dict(torch.load(model_path))
    policy_net.eval()
    
    avg_reward = 0
    
    # 각 모델 별 10번 진행
    for episode in range(10) :    
        state = torch.tensor(env.reset()[0], dtype=torch.float32)
        total_reward = 0

        while total_reward < 501 :
            with torch.no_grad() :
                action = policy_net(state).argmax().item()

            next_state, reward, done, _, _ = env.step(action)
            next_state = torch.tensor(next_state, dtype=torch.float32)

            state = next_state
            total_reward += reward
            
            if done :
                break

        avg_reward += total_reward
    print(f"model {model_i + 1}, Avg Reward: {avg_reward/10}")
    model_i += 1

결과물

 

코드에 궁금한 부분이 있으신 분들은 댓글로 남겨주시면, 답변 드리도록 하겠습니다.

★읽어주셔서 감사합니다★

728x90
반응형
728x90
반응형

728x90

 안녕하세요! 오늘은 기존 작성한 frozen lake 문제SARSA로 진행하는 방법에 대해 포스팅 하겠습니다.

  • SARSA 란?

강화 학습의 한 방법으로, Q-learning과 비슷하지만 행동 선택이 다른 방식입니다.

Q-learning이 미래의 최적 행동을 가정하고 학습한다면, SARSA는 실제로 선택한 행동을 기반으로 학습합니다.

즉 (현재 State, 현재 Action, 현재 Reward, 다음 State, 최적의 행동)이 Q-learning이었다면,

(현재 State, 현재 Action, 현재 Reward, 다음 State, 다음 Action)이 SARSA가 되고, 각각의 앞글자를 따와 SARSA라고 이름을 붙이게 된 것입니다.

 

frozen lake를 Q-learning으로 진행하는 방법은 아래 링크에서 확인해주세요.

https://yhj9855.com/entry/RL-gymnasium-frozen-lake-%EA%B0%95%ED%99%94-%ED%95%99%EC%8A%B5-2

 

[RL] gymnasium frozen lake 강화 학습 - 2

안녕하세요! 오늘은 기존에 작성한 frozen lake 문제를 Q-learning으로 진행하는 방법에 대해 포스팅 하겠습니다. Q-learning이란?강화 학습의 한 방법으로, Q라는 테이블을 이용하는 것입니다.Q 테이블

yhj9855.com

 

frozen lake에 관한 전체적인 설명은 아래 포스팅에서 진행하고 있으니, 먼저 확인해주세요!

https://yhj9855.com/entry/RL-gymnasium-frozen-lake-%EA%B0%95%ED%99%94-%ED%95%99%EC%8A%B5-1

 

[RL] gymnasium frozen lake 강화 학습 - 1

안녕하세요! 오늘은 gymnasium에서 제공하는 frozen lake 문제에 대한 설명에 대해 포스팅 하겠습니다. 강화 학습이란?행동을 통해 얻는 보상을 기반으로 학습하는 AI의 한 분야입니다.흔히 Reinforcemen

yhj9855.com


그럼 본격적으로 SARSA로 frozen lake 문제를 풀어보도록 하겠습니다.

SARSA로 frozen lake를 풀 때, 크게 두 가지를 생각하시면 됩니다.

  1. Q 값을 바탕으로 행동을 선택
  2. 선택한 행동을 SARSA 공식을 사용해서 Q 값 업데이트

위의 두 가지를 하나씩 자세히 살펴보겠습니다.

Q 값을 바탕으로 행동을 선택

해당 부분은 현재까지 업데이트 된 Q 값을 바탕으로 행동을 선택하는 것입니다.

기본적으로는 Q 값이 가장 높은 행동을 선택하면 됩니다.

 

하지만 여기에는 한 가지 문제점이 있습니다.

Q 값이 제대로 업데이트가 될 때까지 충분한 탐험을 진행하지 못했을 경우, 제대로 된 행동을 추출할 수 없다는 것입니다.

예를 들어, 초반에는 Q 값이 모두 0이기 때문에 (0, 0)에서 왼쪽으로 가는 행동만 선택하기 때문에 게임을 진행할 수가 없습니다.

 

저희는 이 문제를 해결하기 위해, ϵ-greedy 방법을 사용할 수 있습니다.

  • ϵ-greedy란?

탐험과 이용의 균형을 맞추기 위한 행동 선택 방법으로, 아래 공식을 따릅니다.

여기서 ϵ은 0과 1 사이의 값으로 ϵ 확률 만큼은 랜덤하게 행동을 하게 하여 탐험을 진행하도록 하고, (1-ϵ) 확률 만큼 Q 값이 가장 높은 행동을 선택하도록 합니다.

해당 ϵ을 초반에 높게 설정하고 점차 ϵ을 줄임으로써, 초반에는 랜덤 행동을 통한 탐험을 하게 하고 점차 Q 값을 이용하도록 행동을 선택할 수 있습니다.

 

ϵ-greedy를 활용하여 행동을 선택하는 코드는 아래와 같습니다.

# 초기 값은 보통 1로 설정
epsilon = 1.0
train = True

# ϵ-greedy를 활용한 행동 선택
def select_action(state) :
	# 훈련을 할 경우에는 ϵ-greedy 방법을 사용
   	# 테스트를 진행할 때는 온전히 Q 값만 사용
   	# np.random.rand()를 넣어, 후반에도 종종 탐험을 할 수 있도록 함
    if np.random.rand() < epsilon and train :
        action = np.random.choice([0, 1, 2, 3])
    else :
        action = np.argmax(Q[state])
    return action

선택한 행동을 SARSA 공식을 사용해서 Q 값 업데이트

해당 부분은 위에서 선택한 행동을 환경에서 실행해보고, 그 결과 값을 SARSA 공식에 맞게 Q 값을 업데이트 하는 것입니다.

코드로 들어가기 전에 먼저 Q 테이블을 업데이트하는 공식을 먼저 살펴보겠습니다.

해당 수식은 Q(s, a)를 업데이트 하는데, 특정 학습률 α에 있어 (1- α)만큼 현재의 Q 값 α만큼의 (보상값 r + 할인율  γ * 다음 state와 action의 Q값 Q(s', a'))를 반영한다는 의미입니다.

Q-learning을 배우신 분들은 아시겠지만, Q-learning에서의 maxQ값특정 행동 a'의 Q 값인 Q(s', a')로 바뀐 것을 알 수 있습니다.

  • 학습률 α

값이 높을수록 다음 행동 값 즉, 새로운 정보를 더 많이 반영한다는 것이고, 낮을수록 현재의 Q 값 즉, 기존의 경험을 더 많이 유지한다는 의미입니다.

  • 할인율 γ

미래 보상의 중요도를 나타내는 지표로, 보통은 미래의 보상에 너무 의존하지 않도록 1보다 약간 작은 수로 지정하는 것이 보통입니다.

  • Q(s', a')

실제로 선택한 다음 행동 a'에 대한 Q 값으로, 위의 행동 선택을 기반하여 다음 state에서 실제 action을 고른 값입니다.

이렇게 실제 행동을 기반으로 Q 값을 업데이트 하기 때문에, 안정적이지만 그만큼 느릴 수 있습니다.

하지만, frozen lake는 공간이 작은 문제라서 Q-learning과 크게 결과 차이는 없으실 거예요:)

 

해당 공식을 바탕으로 SARSA를 진행하는 코드는 아래와 같습니다.

# 학습을 진행할 때는 render 모드 비활성화
env = gym.make('FrozenLake-v1', desc=None, map_name=map_size, is_slippery=is_slippery)
env.reset()
# 환경의 행동 범위 : 여기서는 상, 하, 좌, 우 총 4개
action_size = env.action_space.n

# defaultdict은 키가 없을 때 자동으로 기본값을 생성하기 때문에 강화 학습에서 많이 사용
Q = defaultdict(lambda: np.zeros(action_size))

alpha = 0.1
gamma = 0.99
# 총 학습을 진행할 에피소드 수
max_episode = 10000

def learn() :
    reward_list = []
    for i in range(1, max_episode+1) :
        # 100번째 마다 학습이 진행되고 있음을 출력
        if i % 100 == 0 :
            # 해당 에피소드까지 진행된 모든 보상의 평균을 구함
            avg_reward = sum(reward_list)/100
            print("\rEpisode {}/{} || average reward {}".format(i, max_episode, avg_reward), end="")
            reward_list = []
        # 에피소드를 처음 시작할 때 reset
        state, _ = env.reset()
        done = False
        all_reward = 0
        # 에피소드가 종료될 때까지 반복
        while not done :
        	# Q 테이블을 바탕으로 action을 고르는 함수
            action = select_action(state)
            # state, reward, done 외 사용하지 않기 때문에 _ 처리
            new_state, reward, done, _, _ = env.step(action)
            next_action = select_action(new_state)
            # SARSA
            Q[state][action] = (1-alpha)*Q[state][action] + alpha*(reward + gamma*Q[new_state][next_action])
            all_reward += reward
            state = new_state
        # 50번째 에피소드 마다 ϵ 값을 줄여줌
        if i % 50 == 0 :
            epsilon *= 0.99
        reward_list.append(all_reward)

 

위의 두 가지 과정을 합치면 SARSA로 frozen lake를 풀 수 있는 코드가 완성됩니다!

학습 후 테스트를 진행하고 싶으신 경우에는 render를 킨 환경을 다시 세팅해서 해주시면 됩니다.

전체 코드

행동 선택, 학습, 테스트 과정을 모두 포함한 전체 코드는 아래와 같습니다.

import gymnasium as gym
from collections import defaultdict
import numpy as np

# 미끄러짐 옵션 True/False 선택 가능
is_slippery = True
# 8x8 중에 선택 가능
map_size = '4x4'
env = gym.make('FrozenLake-v1', desc=None, map_name=map_size, is_slippery=is_slippery)
env.reset()
action_size = env.action_space.n

Q = defaultdict(lambda: np.zeros(action_size))

alpha = 0.1
gamma = 0.99
epsilon = 1.0
train = True
max_episode = 100000


def select_action(state) :
    if np.random.rand() < epsilon and train :
        action = np.random.choice([0, 1, 2, 3])
    else :
        action = np.argmax(Q[state])
    return action

def learn() :
    global epsilon
    reward_list = []
    for i in range(1, max_episode+1) :
        # 100번째 마다 학습이 진행되고 있음을 출력
        if i % 100 == 0 :
            # 해당 에피소드까지 진행된 모든 보상의 평균을 구함
            avg_reward = sum(reward_list)/100
            print("\rEpisode {}/{} || average reward {}".format(i, max_episode, avg_reward), end="")
            reward_list = []
        state, _ = env.reset()
        done = False
        all_reward = 0
        while not done :
            action = select_action(state)
            new_state, reward, done, _, _ = env.step(action)
            next_action = select_action(new_state)
            # SARSA
            Q[state][action] = (1-alpha)*Q[state][action] + alpha*(reward + gamma*Q[new_state][next_action])
            all_reward += reward
            state = new_state
        if i % 50 == 0 :
            epsilon *= 0.99
        reward_list.append(all_reward)

# 학습한 Q를 바탕으로 frozen lake 테스트
def testing_after_learning():
	# render를 켜야 제대로 학습이 되었는지 확인할 수 있음
    env = gym.make('FrozenLake-v1', desc=None, map_name=map_size, is_slippery=is_slippery, render_mode='human')
    total_test_episode = 10
    rewards = []
    for episode in range(total_test_episode):
        state, _ = env.reset()
        episode_reward = 0
        while True: 
            action = select_action(state)
            new_state, reward, done, _, _ = env.step(action)
            episode_reward += reward
            if done:
                rewards.append(episode_reward)
                break
            state = new_state
    print("")
    print("avg: " + str(sum(rewards) / total_test_episode))

if __name__ == "__main__" :
    learn()
    testing_after_learning()

 

테스트를 진행하면서 is_slippery 옵션을 껐을 경우에는 1.0 보상을 받으면 성공이고, is_slippery 옵션을 켰을 경우에는 70% 이상 1.0 보상을 받으면 성공이라고 보실 수 있습니다.

 

추가로  is_slippery 옵션을 켰을 경우에는 학습을 많이 진행해야 어느 정도 수렴하시는 걸 보실 수 있습니다!

아무래도 model-free로 진행을 하니까 많이 느리더라구요ㅠㅠ

  • model-based

model-free가 아닌 어느 정도 model-based로 빠르게 학습을 하고 싶으신 경우 아래 상황을 고려할 수 있습니다.

  1. 행동 한 번을 진행할 때마다 reward에 - 진행
    → RL이 최단 경로로 진행하려는 경향을 학습할 수 있음
  2. 구멍에 빠졌을 경우, reward에 크게 - 진행
    → 구멍에 빠지지 않는 쪽으로 빠르게 학습할 수 있음
  3. 도착했을 경우, reward를 크게 추가
    → 도착 지점에 확실히 도착하기 위해 큰 reward를 지급

그 외에도 벽에 부딪히거나 하는 등 맵을 알고 있기 때문에 환경에 맞게 reward를 추가로 주거나 마이너스를 진행하여, model-based 모델을 만들 수도 있습니다.

 

그래도 강화 학습을 제대로 알기 위해서는 model-free로 진행해보는 것을 추천드립니다!

코드에 대해 궁금한 부분이 있으신 분들은 댓글로 남겨주시면, 답변 드리도록 하겠습니다.

★읽어주셔서 감사합니다★

728x90
반응형
728x90
반응형
728x90

 

안녕하세요! 오늘은 기존에 작성한 frozen lake 문제를 Q-learning으로 진행하는 방법에 대해 포스팅 하겠습니다.

 

  • Q-learning이란?

강화 학습의 한 방법으로, Q라는 테이블을 이용하는 것입니다.

Q 테이블 내 값은 특정 상황에서 어떤 행동을 했을 때의 보상 값의 큰 정도를 나타내는 것으로, 학습이 진행되면서 해당 값들을 업데이트 하여 최적의 행동을 찾는 것입니다.

 

frozen lake에 관한 전체적인 설명은 아래 포스팅에서 진행하고 있으니, 먼저 확인해주세요!

https://yhj9855.com/entry/RL-gymnasium-frozen-lake-%EA%B0%95%ED%99%94-%ED%95%99%EC%8A%B5-1

 

[RL] gymnasium frozen lake 강화 학습 - 1

안녕하세요! 오늘은 gymnasium에서 제공하는 frozen lake 문제에 대한 설명에 대해 포스팅 하겠습니다. 강화 학습이란?행동을 통해 얻는 보상을 기반으로 학습하는 AI의 한 분야입니다.흔히 Reinforcemen

yhj9855.com


그럼 본격적으로 Q-learing으로 frozen lake 문제를 풀어보도록 하겠습니다.

Q-learning으로 frozen lake를 풀 때, 크게 두 가지를 생각하시면 됩니다.

  1. Q 값을 바탕으로 행동을 선택
  2. 선택한 행동을 Q-learning 공식을 사용해서 Q 값 업데이트

위의 두 가지를 하나씩 자세히 살펴보겠습니다.

Q 값을 바탕으로 행동을 선택

해당 부분은 현재까지 업데이트 된 Q 값을 바탕으로 행동을 선택하는 것입니다.

기본적으로는 Q 값이 가장 높은 행동을 선택하면 됩니다.

 

하지만 여기에는 한 가지 문제점이 있습니다.

Q 값이 제대로 업데이트가 될 때까지 충분한 탐험을 진행하지 못했을 경우, 제대로 된 행동을 추출할 수 없다는 것입니다.

예를 들어, 초반에는 Q 값이 모두 0이기 때문에 (0, 0)에서 왼쪽으로 가는 행동만 선택하기 때문에 게임을 진행할 수가 없습니다.

 

저희는 이 문제를 해결하기 위해, ϵ-greedy 방법을 사용할 수 있습니다.

  • ϵ-greedy란?

탐험과 이용의 균형을 맞추기 위한 행동 선택 방법으로, 아래 공식을 따릅니다.

여기서 ϵ은 0과 1 사이의 값으로 ϵ 확률 만큼은 랜덤하게 행동을 하게 하여 탐험을 진행하도록 하고, (1-ϵ) 확률 만큼 Q 값이 가장 높은 행동을 선택하도록 합니다.

해당 ϵ을 초반에 높게 설정하고 점차 ϵ을 줄임으로써, 초반에는 랜덤 행동을 통한 탐험을 하게 하고 점차 Q 값을 이용하도록 행동을 선택할 수 있습니다.

 

ϵ-greedy를 활용하여 행동을 선택하는 코드는 아래와 같습니다.

# 초기 값은 보통 1로 설정
epsilon = 1.0
train = True

# ϵ-greedy를 활용한 행동 선택
def select_action(state) :
	# 훈련을 할 경우에는 ϵ-greedy 방법을 사용
   	# 테스트를 진행할 때는 온전히 Q 값만 사용
   	# np.random.rand()를 넣어, 후반에도 종종 탐험을 할 수 있도록 함
    if np.random.rand() < epsilon and train :
        action = np.random.choice([0, 1, 2, 3])
    else :
        action = np.argmax(Q[state])
    return action

 

선택한 행동을 Q-learning 공식을 사용해서 Q 값 업데이트

해당 부분은 위에서 선택한 행동을 환경에서 실행해보고, 그 결과 값을 Q-learning 공식에 맞게 Q 값을 업데이트 하는 것입니다.

코드로 들어가기 전에 먼저 Q 테이블을 업데이트하는 공식을 먼저 살펴보겠습니다.

해당 수식은 Q(s, a)를 업데이트 하는데, 특정 학습률 α에 있어 (1- α)만큼 현재의 Q 값α만큼의 (보상값 r + 할인율  γ * 다음 state의 가장 높은 Q값 maxQ(s', a'))를 반영한다는 의미입니다.

  • 학습률 α

값이 높을수록 다음 행동 값 즉, 새로운 정보를 더 많이 반영한다는 것이고, 낮을수록 현재의 Q 값 즉, 기존의 경험을 더 많이 유지한다는 의미입니다.

  • 할인율 γ

미래 보상의 중요도를 나타내는 지표로, 보통은 미래의 보상에 너무 의존하지 않도록 1보다 약간 작은 수로 지정하는 것이 보통입니다.

  • maxQ(s', a')

다음 상태인 s'에서 가능한 모든 행동 중 가장 높은 Q 값을 의미하며, s'은 현재 state에서 위에서 고른 행동을 실행한 결과 값이라고 보시면 됩니다.

 

이제 해당 공식을 바탕으로 Q-learning을 하는 코드는 아래와 같습니다.

# 학습을 진행할 때는 render 모드 비활성화
env = gym.make('FrozenLake-v1', desc=None, map_name=map_size, is_slippery=is_slippery)
env.reset()
# 환경의 행동 범위 : 여기서는 상, 하, 좌, 우 총 4개
action_size = env.action_space.n

# defaultdict은 키가 없을 때 자동으로 기본값을 생성하기 때문에 강화 학습에서 많이 사용
Q = defaultdict(lambda: np.zeros(action_size))

alpha = 0.1
gamma = 0.99
# 총 학습을 진행할 에피소드 수
max_episode = 10000

def learn() :
    reward_list = []
    for i in range(1, max_episode+1) :
        # 100번째 마다 학습이 진행되고 있음을 출력
        if i % 100 == 0 :
            # 해당 에피소드까지 진행된 모든 보상의 평균을 구함
            avg_reward = sum(reward_list)/100
            print("\rEpisode {}/{} || average reward {}".format(i, max_episode, avg_reward), end="")
            reward_list = []
        # 에피소드를 처음 시작할 때 reset
        state, _ = env.reset()
        done = False
        all_reward = 0
        # 에피소드가 종료될 때까지 반복
        while not done :
        	# Q 테이블을 바탕으로 action을 고르는 함수
            action = select_action(state)
            # state, reward, done 외 사용하지 않기 때문에 _ 처리
            new_state, reward, done, _, _ = env.step(action)
            # Q-learning 공식
            Q[state][action] = (1-alpha)*Q[state][action] + alpha*(reward + gamma*np.max(Q[new_state]))
            all_reward += reward
            state = new_state
        # 50번째 에피소드 마다 ϵ 값을 줄여줌
        if i % 50 == 0 :
            epsilon *= 0.99
        reward_list.append(all_reward)

위의 두 가지 과정을 합치면 Q-learing으로 frozen lake를 풀 수 있는 코드가 완성됩니다!

학습 후 테스트를 진행하고 싶으신 경우에는 render를 킨 환경을 다시 세팅해서 해주시면 됩니다.

 

전체 코드

행동 선택, 학습, 테스트 과정을 모두 포함한 전체 코드는 아래와 같습니다.

import gymnasium as gym
from collections import defaultdict
import numpy as np

# 미끄러짐 옵션 True/False 선택 가능
is_slippery = True
# 8x8 중에 선택 가능
map_size = '4x4'
env = gym.make('FrozenLake-v1', desc=None, map_name=map_size, is_slippery=is_slippery)
env.reset()
action_size = env.action_space.n

Q = defaultdict(lambda: np.zeros(action_size))

alpha = 0.1
gamma = 0.99
epsilon = 1.0
train = True
max_episode = 100000


def select_action(state) :
    if np.random.rand() < epsilon and train :
        action = np.random.choice([0, 1, 2, 3])
    else :
        action = np.argmax(Q[state])
    return action

def learn() :
    global epsilon
    reward_list = []
    for i in range(1, max_episode+1) :
        # 100번째 마다 학습이 진행되고 있음을 출력
        if i % 100 == 0 :
            # 해당 에피소드까지 진행된 모든 보상의 평균을 구함
            avg_reward = sum(reward_list)/100
            print("\rEpisode {}/{} || average reward {}".format(i, max_episode, avg_reward), end="")
            reward_list = []
        state, _ = env.reset()
        done = False
        all_reward = 0
        while not done :
            action = select_action(state)
            new_state, reward, done, _, _ = env.step(action)
            Q[state][action] = (1-alpha)*Q[state][action] + alpha*(reward + gamma*np.max(Q[new_state]))
            all_reward += reward
            state = new_state
        if i % 50 == 0 :
            epsilon *= 0.99
        reward_list.append(all_reward)

# 학습한 Q를 바탕으로 frozen lake 테스트
def testing_after_learning():
	# render를 켜야 제대로 학습이 되었는지 확인할 수 있음
    env = gym.make('FrozenLake-v1', desc=None, map_name=map_size, is_slippery=is_slippery, render_mode='human')
    total_test_episode = 10
    rewards = []
    for episode in range(total_test_episode):
        state, _ = env.reset()
        episode_reward = 0
        while True: 
            action = select_action(state)
            new_state, reward, done, _, _ = env.step(action)
            episode_reward += reward
            if done:
                rewards.append(episode_reward)
                break
            state = new_state
    print("")
    print("avg: " + str(sum(rewards) / total_test_episode))

if __name__ == "__main__" :
    learn()
    testing_after_learning()

 

 

테스트를 진행하면서 is_slippery 옵션을 껐을 경우에는 1.0 보상을 받으면 성공이고, is_slippery 옵션을 켰을 경우에는 70% 이상 1.0 보상을 받으면 성공이라고 보실 수 있습니다.

 

추가로  is_slippery 옵션을 켰을 경우에는 학습을 많이 진행해야 어느 정도 수렴하시는 걸 보실 수 있습니다!

아무래도 model-free로 진행을 하니까 많이 느리더라구요ㅠㅠ

  • model-based

model-free가 아닌 어느 정도 model-based로 빠르게 학습을 하고 싶으신 경우 아래 상황을 고려할 수 있습니다.

  1. 행동 한 번을 진행할 때마다 reward에 - 진행
    → RL이 최단 경로로 진행하려는 경향을 학습할 수 있음
  2. 구멍에 빠졌을 경우, reward에 크게 - 진행
    → 구멍에 빠지지 않는 쪽으로 빠르게 학습할 수 있음
  3. 도착했을 경우, reward를 크게 추가
    → 도착 지점에 확실히 도착하기 위해 큰 reward를 지급

그 외에도 벽에 부딪히거나 하는 등 맵을 알고 있기 때문에 환경에 맞게 reward를 추가로 주거나 마이너스를 진행하여, model-based 모델을 만들 수도 있습니다.

 

그래도 강화 학습을 제대로 알기 위해서는 model-free로 진행해보는 것을 추천드립니다!

코드에 대해 궁금한 부분이 있으신 분들은 댓글로 남겨주시면, 답변 드리도록 하겠습니다.

★읽어주셔서 감사합니다★

728x90
반응형
728x90
반응형
728x90

 

안녕하세요! 오늘은 gymnasium에서 제공하는 frozen lake 문제에 대한 설명에 대해 포스팅 하겠습니다.

 

  • 강화 학습이란?

행동을 통해 얻는 보상을 기반으로 학습하는 AI의 한 분야입니다.

흔히 Reinforcement Learning 줄여서, RL이라고 부르는 강화 학습은 컴퓨터가 스스로 경험을 쌓으면서 어떤 행동이 가장 좋은 보상을 얻는지 배우는 과정이라고 볼 수 있습니다.

 

저는 frozen lake 문제를 Q-learningSARSA 두 가지 방법으로 풀어보았는데요!

각각 전체 코드는 포스팅 가장 아래에 있습니다.


  • frozen lake 규칙

우선 gymnasium에서 제공하는 frozen lake 문제는 아래 링크에서 자세하게 보실 수 있습니다.

https://gymnasium.farama.org/environments/toy_text/frozen_lake/

 

Gymnasium Documentation

A standard API for reinforcement learning and a diverse set of reference environments (formerly Gym)

gymnasium.farama.org

 

frozen lake의 기본 맵은 아래 사진처럼 생겼습니다.

  1. frozen lake의 기본 맵은 4x4 혹은 8x8로 존재
  2. 맵 내 구멍이 존재
    → 구멍의 수나 위치는 조절할 수 없음
  3. 선물에 있는 곳에 도착하거나, 구멍에 빠지면 에피소드 종료
    물에 있는 곳에 도착해야만 보상 1을 획득할 수 있음
  4. 움직임은 상, 하, 좌, 우 존재
  5. 얼음 내 미끄러짐을 키고 끌 수 있음
    → 미끄러짐을 킬 경우, 각 얼음마다 특정 확률로 다른 곳으로 움직이는 확률이 존재
    → 각 얼음마다 존재하는 미끄러질 확률을 조절할 수 없음

저희는 위의 규칙을 잘 생각하여, 구멍에 빠지지 않고 선물에 도착하는 경로를 학습해주어야 합니다.

  • 필요한 라이브러리 설치

frozen lake는 gymnasium에서 제공하고 있기 때문에 가장 먼저 gymnasium을 설치해주시면 됩니다!

그리고 render 모드 활성화를 진행하기 위해 gymnasium[toy-text] 설치도 같이 진행해주세요.

두 패키지 모두 pip install로 설치해주시면 금방 설치할 수 있습니다.

import gymnasium as gym
from collections import defaultdict
import numpy as np
  • frozen lake 환경 세팅

필요한 라이브러리를 설치한 후에는 frozen lake 환경을 세팅해봅니다.

우선 환경 세팅을 하는 코드는 아래와 같습니다.

# 미끄러지는 얼음을 만들지 결정하는 변수
is_slippery = False
# 맵 사이즈
map_size = '4x4'
# frozen lake 환경 설정
# render 활성화 환경
env = gym.make('FrozenLake-v1', desc=None, map_name=map_size, is_slippery=is_slippery, render_mode='human')
# 환경을 초기화하는 함수
env.reset()

env.reset()의 경우 기본 기능은 환경을 초기화하는 함수지만, 환경을 세팅한 다음에 해당 코드를 부르지 않으면 오류가 발생하니 반드시 환경 사용 전에 env.reset()을 먼저 불러주세요!

  • frozen lake 테스트 진행하기

이제 만든 환경을 바탕으로 gymnasium에서 제공하는 frozen lake 테스트를 진행해봅시다!

테스트를 진행하는 코드는 아래와 같습니다.

while True:
    # 0 : 왼쪽, 1 : 아래, 2 : 오른쪽, 3 : 위
    action = input("이동할 방향: ")
    if action not in ['0','1','2','3']:
        continue
    action = int(action)
    # step() : 환경에 액션을 넣고 실행하는 함수
    state, reward, done, info, prob = env.step(action)
    print("위치: ", state, "행동: ", action, "보상: ", reward)
    print()
    if done:
        print("에피소드 종료", reward)
        break

해당 코드를 실행해보면, 4x4 맵이 있는 창에 입력한 행동대로 움직이는 것을 확인할 수 있습니다.

구멍에 빠지거나 선물에 도착하기 전에는 계속 움직일 수 있으니, 환경을 이해하기 위해 편하게 테스트 진행해보세요!

맵을 키우거나 미끄러지는 얼음을 키고 테스트 해보시는 것도 추천드립니다.

 


여기까지가 frozen lake 환경 세팅이었습니다.

이번 포스팅에서는 Q-learning과 SARSA에 관한 model-free 전체 코드만 업로드 되어 있습니다.

두 가지 모두 is_slippery가 False일 경우에는 1.0 만점을 받았고, is_silppery가 True일 경우에는 0.8 이상의 보상을 획득했습니다.

 

Q-learning에 관한 자세한 내용이 궁금하신 분들은 아래 링크에서 확인해주세요!

https://yhj9855.com/entry/RL-gymnasium-frozen-lake-%EA%B0%95%ED%99%94-%ED%95%99%EC%8A%B5-2

 

[RL] gymnasium frozen lake 강화 학습 - 2

안녕하세요! 오늘은 기존에 작성한 frozen lake 문제를 Q-learning으로 진행하는 방법에 대해 포스팅 하겠습니다. Q-learning이란?강화 학습의 한 방법으로, Q라는 테이블을 이용하는 것입니다.Q 테이블

yhj9855.com

 

SARSA에 관한 자세한 내용이 궁금하진 분들은 아래 링크에서 확인해주세요!

https://yhj9855.com/entry/RL-gymnasium-frozen-lake-%EA%B0%95%ED%99%94-%ED%95%99%EC%8A%B5-SARSA

 

[Q-learning 전체 코드]

import gymnasium as gym
from collections import defaultdict
import numpy as np

is_slippery = True
map_size = '8x8'
env = gym.make('FrozenLake-v1', desc=None, map_name=map_size, is_slippery=is_slippery)
env.reset()
action_size = env.action_space.n

Q = defaultdict(lambda: np.zeros(action_size))

alpha = 0.1
gamma = 0.99
epsilon = 1.0
train = True
max_episode = 100000


def select_action(state) :
    if np.random.rand() < epsilon and train :
        action = np.random.choice([0, 1, 2, 3])
    else :
        action = np.argmax(Q[state])
    return action

def learn() :
    global epsilon
    reward_list = []
    for i in range(1, max_episode+1) :
        # 100번째 마다 학습이 진행되고 있음을 출력
        if i % 100 == 0 :
            # 해당 에피소드까지 진행된 모든 보상의 평균을 구함
            avg_reward = sum(reward_list)/100
            print("\rEpisode {}/{} || average reward {}".format(i, max_episode, avg_reward), end="")
            reward_list = []
        # 에피소드를 처음 시작할 때는 reset을 해줘야 함
        state, _ = env.reset()
        done = False
        all_reward = 0
        # 에피소드가 종료될 때까지 반복
        while not done :
            action = select_action(state)
            new_state, reward, done, _, _ = env.step(action)
            # Q 러닝
            Q[state][action] = (1-alpha)*Q[state][action] + alpha*(reward + gamma*np.max(Q[new_state]))
            all_reward += reward
            state = new_state
        if i % 50 == 0 :
            epsilon *= 0.99
        reward_list.append(all_reward)

def testing_after_learning():
    env = gym.make('FrozenLake-v1', desc=None, map_name=map_size, is_slippery=is_slippery, render_mode='human')
    total_test_episode = 10
    rewards = []
    for episode in range(total_test_episode):
        state, _ = env.reset()
        episode_reward = 0
        while True: 
            action = select_action(state)
            new_state, reward, done, _, _ = env.step(action)
            episode_reward += reward
            if done:
                rewards.append(episode_reward)
                break
            state = new_state
    print("")
    print("avg: " + str(sum(rewards) / total_test_episode))

if __name__ == "__main__" :
    learn()
    testing_after_learning()

결과물

is_slippery = False / True

[SARSA 전체 코드]

import gymnasium as gym
from collections import defaultdict
import numpy as np

is_slippery = True
map_size = '4x4'
env = gym.make('FrozenLake-v1', desc=None, map_name=map_size, is_slippery=is_slippery)
env.reset()
action_size = env.action_space.n

Q = defaultdict(lambda: np.zeros(action_size))

alpha = 0.1
gamma = 0.99
epsilon = 1.0
train = True
max_episode = 100000


def select_action(state) :
    if np.random.rand() < epsilon and train :
        action = np.random.choice([0, 1, 2, 3])
    else :
        action = np.argmax(Q[state])
    return action

def learn() :
    global epsilon
    reward_list = []
    for i in range(1, max_episode+1) :
        # 100번째 마다 학습이 진행되고 있음을 출력
        if i % 100 == 0 :
            # 해당 에피소드까지 진행된 모든 보상의 평균을 구함
            avg_reward = sum(reward_list)/100
            print("\rEpisode {}/{} || average reward {}".format(i, max_episode, avg_reward), end="")
            reward_list = []
        # 에피소드를 처음 시작할 때는 reset을 해줘야 함
        state, _ = env.reset()
        done = False
        all_reward = 0
        # 에피소드가 종료될 때까지 반복
        while not done :
            action = select_action(state)
            new_state, reward, done, _, _ = env.step(action)
            next_action = select_action(new_state)
            # SARSA
            Q[state][action] = (1-alpha)*Q[state][action] + alpha*(reward + gamma*Q[new_state][next_action])
            all_reward += reward
            state = new_state
        if i % 50 == 0 :
            epsilon *= 0.99
        reward_list.append(all_reward)

def testing_after_learning():
    env = gym.make('FrozenLake-v1', desc=None, map_name=map_size, is_slippery=is_slippery, render_mode='human')
    total_test_episode = 10
    rewards = []
    for episode in range(total_test_episode):
        state, _ = env.reset()
        episode_reward = 0
        while True: 
            action = select_action(state)
            new_state, reward, done, _, _ = env.step(action)
            episode_reward += reward
            if done:
                rewards.append(episode_reward)
                break
            state = new_state
    print("")
    print("avg: " + str(sum(rewards) / total_test_episode))

if __name__ == "__main__" :
    learn()
    testing_after_learning()

결과물

is_slippery = False / True

 

코드에 궁금한 부분이 있으신 분들은 댓글로 남겨주시면, 답변 드리도록 하겠습니다.

★읽어주셔서 감사합니다★

 

728x90
반응형
728x90
반응형

 

728x90

안녕하세요! 오늘은 데이터 분석에서 정말 중요한 그래프 색상을 정리하는 포스팅을 진행하도록 하겠습니다.

 

데이터 분석에서 시각화는 정말 중요한데요.

똑같이 데이터 분석을 진행했다고 해도, 얼만큼 잘 표현할 수 있는지에 따라 굉장히 다른 평가를 받을 수 있습니다.

저도 실제로 회사에서 그래프 색상 사용 덕분에 보고서의 평가가 훨씬 좋았던 경험이 많았기 때문에 파이썬에서 사용할 수 있는 색상을 정리해보고자 합니다.

 

  • 단일 색상

단일 색상을 이미 지정된 색상을 단일 알파벳으로 지정하는 것, 색상의 이름을 직접 지정하는 것으로 구분할 수 있습니다.

  • 단일 색상 사용 예시

단일 색상인 lightskybluelightcoral을 사용한 예시 입니다.

이 두 색은 제가 자주 사용하는 색 조합인데요!

두 개의 비교군을 보여줄 때 사용하는데, 눈이 아프지 않아 좋습니다.

plt.figure(figsize=(12, 8))  # 그래프 크기 설정
lineplot = sns.lineplot(x='a', y='b', hue='c', data=mini_data, palette=['lightskyblue', 'lightcoral'])
line_colors = {line.get_label(): line.get_color() for line in lineplot.lines}
line_color = {'A': 'deepskyblue', 'B': 'red'}

for name, group in mini_data.groupby('c') :
    for x, y in zip(group['a'], group['b']):
        if x%5 == 0 :
            plt.text(x, y+1.1, f'{y:.1f}%', color=line_color[name], ha='right', va='bottom', fontsize=12, fontweight='bold')
            plt.scatter(x, y, color=line_colors[name], s=70)
        else :
            plt.text(x, y+1.1, f'{y:.1f}%', color=line_color[name], ha='right', va='bottom', fontsize=8, fontweight='bold')
            plt.scatter(x, y, color=line_colors[name], s=30)

  • 컬러맵 (color map)

컬러맵은 색상의 분포를 의미합니다.

산점도나 하트맵 같이 단일 색상으로 정하기 어려운 그래프의 경우 컬러맵을 사용하는 경우가 많습니다.

 

  • 컬러맵 사용 예시

컬러 맵인 Reds coolwarm을 사용한 예시 입니다.

개인적으로 히트맵은 Reds와 같이 비슷한 색상의 컬러 맵을 많이 사용하고, 워드클라우드 같이 데이터가 뚜렷하게 구분되어야 할 경우에는 다른 색상이 많은 컬러 맵을 사용하는 것 같아요!

plt.figure(figsize=(10, 10))
sns.heatmap(df, annot=True, cmap='Reds', fmt='.2f', linewidths = 0.1,annot_kws={'size': 12}, cbar=False)

Naver = np.array(Image.open("./A.png"))
plt.figure(figsize=(30,30))
wc = WordCloud(        relative_scaling=0.2,mask = Naver,
                       font_path="/Windows/Fonts/Cafe24Dangdanghae-v2.0.otf",
                       background_color="white",
                       min_font_size=1,
                       max_font_size=50,
                       max_words=100,
                       colormap = 'coolwarm'
                     ).generate_from_frequencies(wordcloud_data)
plt.imshow(wc)
plt.axis('off')
plt.show()

그 외에도 컬러 맵을 리스트로 지정한 후 하나씩 단일 색상으로 사용할 수도 있습니다!

저는 개인적으로 Qualitative colormaps 같은 경우, 제가 눈으로 색상을 하나씩 확인할 수 있어서 해당 컬러맵은 단일 색상처럼 사용하는 경우도 자주 있는 것 같습니다.

# Pastel1 컬러맵 불러오기
cmap = plt.get_cmap('Pastel1')
# 여섯 번째 색상 선택
color = cmap(5)

plt.figure(figsize=(8, 5))
plt.bar(categories, values, color=color, edgecolor='black')
plt.tight_layout()
plt.show()

 

여기까지 파이썬에서 사용할 수 있는 색상을 정리해보았습니다!

해당 색상을 잘 활용하여 보고서의 퀄리티를 높이는 시각화를 하시면 좋겠습니다:)

★읽어주셔서 감사합니다★

 

728x90
반응형
728x90
반응형
728x90

 

안녕하세요. 오늘은 기존에 작성한 네이버 뉴스 클로링 코드에서 첫 번째 코드의 자세한 크롤링 과정을 포스팅 하겠습니다.

새롭게 변경된 첫 번째 코드의 자세한 크롤링 과정입니다!

 

네이버 뉴스 크롤링 전체 코드를 확인하고 싶으신 분들은 아래 링크를 확인해주세요!

https://yhj9855.com/entry/Crawling-%EB%84%A4%EC%9D%B4%EB%B2%84-%EB%89%B4%EC%8A%A4-%ED%81%AC%EB%A1%A4%EB%A7%81-%EC%BD%94%EB%93%9C-%EB%B3%80%EA%B2%BD

 

[Crawling] 네이버 뉴스 크롤링 코드 변경

안녕하세요! 네이버 뉴스가 24년 1월 25일부터 페이지가 보여주는 방식이 변경되면서, 이전 포스팅에서 진행했던 첫 번째 코드를 사용할 수 없게 되었습니다. 그래서 변경된 페이지에서 적용되는

yhj9855.com

 

이전에 사용되었던 BeautifulSoup으로 크롤링 했던 코드의 자세한 과정이 궁금하신 분들은 아래 링크를 확인해주세요!

https://yhj9855.com/entry/Crawling-%EB%84%A4%EC%9D%B4%EB%B2%84-%EB%89%B4%EC%8A%A4-%ED%81%AC%EB%A1%A4%EB%A7%81-2

 

[Crawling] 네이버 뉴스 크롤링 - 2

안녕하세요. 오늘은 기존에 작성한 네이버 뉴스 크롤링 코드에서 첫 번째 코드의 자세한 크롤링 과정을 포스팅 하겠습니다. 네이버 뉴스 크롤링 전체 코드를 확인하고 싶으신 분들은 아래 링크

yhj9855.com


첫 번째 코드는 Python으로 특정 날짜의 네이버 게임/리뷰 카테고리의 기사들을 크롤링하는데, 제목과 링크만 가져오는 코드였습니다.

 

  • 기본 링크 설정하기

크롤링을 시작할 때 크롤링하고자 하는 페이지의 링크를 설정하는 것이 가장 중요합니다.

위의 그림처럼 2025년 01월 07일의 게임/리뷰 카테고리의 뉴스를 크롤링하고자 한다면, 원하는 정보가 담긴 링크는 아래와 같습니다.

 

https://news.naver.com/breakingnews/section/105/229?date=20250107

 

해당 링크에서 가장 중요한 것은 date=20250107 부분인데요!

네이버 뉴스의 경우, 날짜를 링크 자체에서 구분할 수 있게 되어 있습니다.

그렇기 때문에 특정 날짜의 기사들을 크롤링하고 싶다면 date 뒤에 연도+월+일을 붙이면 된다는 사실을 알 수 있습니다.

해당 부분을 코드로 변경하면 아래와 같습니다.

# 수정하고자 하는 메인 링크
link = 'https://news.naver.com/breakingnews/section/105/229?date='
# 스크랩 하고 싶은 날짜를 년도월일 나열해준다.
# 날짜를 쉽게 바꾸기 위해 date를 따로 선언해준다.
date = '20250107'
# 메인 링크는 링크에 날짜가 붙은 구조이기 때문에 이렇게 작성해준다.
main_link = link + date
# 기사의 수, 제목, 링크를 받아올 예정이기 때문에 정보를 담아줄 데이터 프레임을 생성한다.
Main_link = pd.DataFrame({'number' : [], 'title' : [], 'link' : []})

 

[크롤링 하고자 하는 URL 찾기 Tip]

더보기
더보기

링크를 확인할 때 오늘 날짜, 첫 페이지와 같이 기본적으로 접속하면 바로 볼 수 있는 URL로 확인하는 건 피하시는게 좋습니다.

기본으로 설정된 URL은 해당 URL만 특별하게 설정되어 있거나, 보이는 링크와 실제 링크가 다른 경우도 종종 있기 때문에 오류가 발생하기 쉽습니다.

아래 그림처럼 '오늘' 날짜의 뉴스 기사에는 date 표시가 되어있지 않기 때문에 초기 링크를 설정할 때 어려움이 있을 수 있습니다.

  • 기사 더보기 클릭 및 URL 확인하기

 

원하는 날짜의 모든 기사를 크롤링하기 위해서는 아래 그림처럼 기사 더보기 버튼을 클릭해서 기사를 끝까지 업로드 해야 합니다.

 

이 때, 중요한 것은 기사를 업로드 해도 URL이 변경되지 않는다는 것입니다!

기사 더보기를 웹 페이지 안의 내용이 달라졌음에도 URL이 달라지지 않기 때문에 이전에 진행했던 정적 크롤링으로는 진행할 수가 없는 것입니다.

 

이렇게 URL이 변경되지 않고, 데이터가 변경되는 것동적 페이지라고 부릅니다.

동적 페이지는 오직 1가지 방법으로만 크롤링이 가능한데요!

바로 Selenium을 활용한 동적 크롤링 입니다.

 

Selenium을 활용한 동적 크롤링을 진행하기 위해서는 크롬 드라이버 설치가 필수적입니다.

크롬 드라이버 설치가 되어 있지 않거나, 자신의 크롬 버전과 크롬 드라이버 버전이 맞지 않으시는 분들은 아래 링크에 설명된 방법을 통해서 크롬 드라이버를 먼저 설치해주셔야 합니다!

https://yhj9855.com/entry/%ED%81%AC%EB%A1%AC%EB%93%9C%EB%9D%BC%EC%9D%B4%EB%B2%84-%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0-%EC%B5%9C%EC%8B%A0%EB%B2%84%EC%A0%84%EA%B3%BC-%EB%8B%A4%EB%A5%B8-%ED%81%AC%EB%A1%AC%EB%93%9C%EB%9D%BC%EC%9D%B4%EB%B2%84-%EC%84%A4%EC%B9%98

 

크롬드라이버 설치하기 (최신버전과 다른 크롬드라이버 설치)

안녕하세요!최근에 크롬 드라이버를 설치하는 페이지가 변경되어 새롭게 포스팅을 하겠습니다! 이전 포스팅이 궁금하신 분들은 아래 포스팅을 참고해주시면 됩니다!https://yhj9855.com/entry/%ED%81%AC

yhj9855.com

 

  • 코드로 기사 더보기 버튼 클릭하기

이제 어떤 방향으로 크롤링을 해야하는지 알았기 때문에 기사를 모두 업로드해서 크롤링하는 방법만 남았습니다.

다음으로 고려해야 할 문제점은 저희가 코드로 기사 더보기 버튼을 클릭해야 하는 것과 기사 더보기 버튼의 끝을 알아야 한다는 것입니다.

아래 그림처럼 기사를 기사 더보기 버튼을 지속적으로 클릭하다보면, 더 이상 해당 버튼이 등장하지 않는 것을 보실 수 있습니다.

해당 부분을 보고 저희가 알 수 있는 것은 기사 더보기 버튼을 계속 클릭하다보면, 더 이상 버튼이 없어서 오류가 발생하는 지점이 생긴다는 것입니다!

 

이제 개발자 도구 창을 열어, 기사 더보기 버튼이 어떤 HTML 구조를 가지고 있는지 확인하는 작업이 필요합니다.

개발자 도구에 대한 자세한 설명은 아래 링크를 확인해주세요!

https://yhj9855.com/entry/%ED%81%AC%EB%A1%AC-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EB%8F%84%EA%B5%AC-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0

 

크롬 개발자 도구 사용하기

안녕하세요. 오늘은 크롤링을 진행하기 위해 반드시 필요한 개발자 도구를 사용하는 법에 대해서 포스팅 하겠습니다. 크롬 개발자 도구란? 크롬 브라우저에 직접 내장된 웹 개발 도구로, 웹 페

yhj9855.com

 

저희는 기사 더보기 버튼의 HTML 구조를 알고 싶은 것이기 때문에 아래 그림처럼 따라합니다.

①번의 아이콘을 클릭한 후, ②번(=원하는 정보)을 클릭하면 원하는 정보의 HTML 구조로 바로 이동이 됩니다.

원하는 정보의 HTML 구조를 확인해보니, 아래와 같이 나왔습니다.

 

<a href="#" class="section_more_inner _CONTENT_LIST_LOAD_MORE_BUTTON" data-persistable="false">기사 더보기</a>

 

[HTML 구조의 뜻]

더보기
더보기
  1. 'a' 태그 : 링크가 담겨져 있는 공간이면 해당 태그를 사용합니다. 현재 버튼을 클릭하면 새로운 기사가 등장하기 때문에 해당 태그를 사용했습니다.
  2. href: 링크의 주소를 가지고 있는 부분입니다. 여기서는 #을 사용하여, 페이지를 변동하지 않겠다는 옵션을 지정해주었습니다.
  3. class: 태그의 속성을 나타내는 부분입니다. 크롤링을 진행할 때 많이 사용되는 부분으로 ID가 없을 경우에는 이름처럼 사용되기도 합니다.
  4. data-* : HTML5에서 지원하는 사용자 정의 데이터 속성입니다. 여기서의 옵션은 특정 동작이나 상태를 저장하지 않도록 지시하는 것을 의미합니다.

 

기사 더보기 버튼의 HTMl 구조를 알았으니, 오류가 발생할 때까지 버튼을 클릭하는 코드를 작성합니다.

service = Service('chromedriver.exe')
driver = webdriver.Chrome(service=service)
driver.get(main_link)
# 웹 페이지 로딩을 기다리는 코드로, 초는 더 짧아도 된다.
time.sleep(3)

# 기사 더보기 버튼
more_button = driver.find_element(By.CLASS_NAME, 'section_more_inner._CONTENT_LIST_LOAD_MORE_BUTTON')

# 기사 더보기가 몇 개가 있을지 모르기 때문에 오류가 날 때까지 누르는 것으로 한다.
# 여기서 발생하는 오류란 버튼을 찾을 수 없다 즉, 버튼이 없을 때 발생하는 오류이다.
while True :
    try :
        more_button.click()
        time.sleep(3)
    except :
        break

위의 코드에서 보면, time.sleep 코드를 사용하는 것을 보실 수 있습니다.

동적 크롤링을 작성하실 때 생각보다 많이 놓치실 수 있는 부분인데요!

동적 크롤링은 코드로 크롬을 띄우고, 직접 웹 페이지에 들어가서 해당 페이지에 있는 내용을 크롤링하는 작업입니다.

그렇기 때문에 웹 페이지 로딩이 되지 않은 상태에서 접근을 시도하면 오류가 발생하는데, 이 때 발생하는 오류가 특정 HTML을 찾을 수 없다는 오류가 발생합니다.

 

이로 인해 초보자분들께서는 웹 페이지가 로딩이 되지 못했다고 생각을 못하고, 코드 내 오류가 있다고만 생각을 하여 한참 시간을 소모하는 경우를 많이 봤습니다!

이를 방지하기 위해서 time.sleep 명령어를 웹 페이지가 변경될 때마다 꼭 넣어주셔야 합니다.

몇 초를 기다리는지는 웹 페이지 안의 영상, 이미지 여부나 인터넷 속도에 따라서 다르지만 보통 1~5초 사이 라고 보시면 됩니다.

 

  • 기사의 제목과 링크 가져오기

마지막으로 크롤링 목적인 기사의 제목과 링크를 가져오기만 하면 첫 번째 코드의 크롤링이 완료됩니다!

버튼을 확인했던 것처럼 개발자 도구에서 기사의 제목과 링크가 어떤 HTML 구조를 가지고 있는지 확인해보겠습니다.

HTML 구조를 확인해보니 아래와 같이 나왔습니다.

<a href="https://n.news.naver.com/mnews/article/119/0002911455" class="sa_text_title _NLOG_IMPRESSION" data-clk="alist" data-rank="34" data-gdid="8812A0A8_000000000000000002911455" data-imp-url="https://n.news.naver.com/mnews/article/119/0002911455">
<strong class="sa_text_strong">LCK컵, 15일 개최…우승 팀은 국제 대회 출전권 획득</strong>
</a>

 

[HTML 구조의 뜻]

더보기
더보기
  1. 'strong' 태그 : HTML에서 텍스트를 굵게 보이기 위한 태그입니다.

 

이전 기사 더보기 버튼의 HTML 구조와 매우 유사한 것을 알 수 있습니다.

저희가 수집해야 할 정보는 아래와 같습니다.

  1. 기사의 제목 : a태그 속 strong태그 텍스트
  2. 기사의 링크 : a태그 속 HTML 구조의 href

해당 부분을 코드로 변경하면 아래와 같습니다.

# 기사의 제목과 링크가 모두 담긴 a태그를 모두 찾는다.
articles = driver.find_elements(By.CLASS_NAME, 'sa_text_title._NLOG_IMPRESSION')
# a태그 내 기사의 제목과 링크를 따로 저장한다.
for i in range(len(articles)) :
	# 기사의 제목
    # strip을 사용하여 눈으로 확인할 수 없는 양 끝의 공백을 제거한다.
    title = articles[i].text.strip()
    # href 부분을 가져온느 방법
    # a태그 내 href를 가져온다.
    link = articles[i].get_attribute('href')
    # 번호는 0부터 시작하기 때문에 1을 더해준다.
    li = [i+1, title, link]
    Main_link.loc[i] = li
  • 액셀 파일로 저장

이제 크롤링 작업은 완료되었고, 크롤링한 데이터프레임을 엑셀 파일로 저장하도록 하겠습니다.

# 엑셀을 잘 관리하기 위해서 크롤링 날짜를 파일 이름에 포함한다.
excel_name = 'news_' + date + '.xlsx'
with pd.ExcelWriter(excel_name) as writer :
    Main_link.to_excel(writer, sheet_name='링크', index=False)

이런 과정을 통해서 특정 날짜의 모든 기사의 제목과 링크를 크롤링하는 코드가 완성되었습니다!!

 

[전체 코드]

import pandas as pd
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
import time
from selenium.webdriver.common.by import By
from openpyxl import *

link = 'https://news.naver.com/breakingnews/section/105/229?date='
date = '20250107'

main_link = link + date 
Main_link = pd.DataFrame({'number' : [], 'title' : [], 'link' : []})

service = Service('chromedriver.exe')
driver = webdriver.Chrome(service=service)
driver.get(main_link)
time.sleep(3)

more_button = driver.find_element(By.CLASS_NAME, 'section_more_inner._CONTENT_LIST_LOAD_MORE_BUTTON')

while True :
    try :
        more_button.click()
        time.sleep(3)
    except :
        break

articles = driver.find_elements(By.CLASS_NAME, 'sa_text_title._NLOG_IMPRESSION')
for i in range(len(articles)) :
    title = articles[i].text.strip()
    link = articles[i].get_attribute('href')
    li = [i+1, title, link]
    Main_link.loc[i] = li


excel_name = 'news_' + date + '.xlsx'
with pd.ExcelWriter(excel_name) as writer :
    Main_link.to_excel(writer, sheet_name='링크', index=False)

 

이전 코드와 마찬가지로 Xpath, CSS_Selector를 사용하지 않았는데, 해당 부분을 사용하면 쉽게 크롤링을 할 수 있지만 HTML 코드가 복잡하거나 크롤링을 배우고 싶은 분들에게는 좋은 방법이 아니라고 생각합니다.

 

기사의 본문을 크롤링하는 두 번째 코드의 자세한 크롤링 코딩 과정을 확인하고 싶으신 분들은 아래 링크를 확인해주세요!!
https://yhj9855.com/entry/Crawling-%EB%84%A4%EC%9D%B4%EB%B2%84-%EB%89%B4%EC%8A%A4-%ED%81%AC%EB%A1%A4%EB%A7%81-3

 

[Crawling] 네이버 뉴스 크롤링 - 3

안녕하세요. 오늘은 기존에 작성한 네이버 뉴스 크롤링 코드에서 두 번째 코드의 자세한 크롤링 과정을 포스팅 하겠습니다. 네이버 뉴스 크롤링 전체 코드를 확인하고 싶으신 분들은 아래 링크

yhj9855.com

 

코드에 대해 궁금한 점이 있으신 분들은 댓글로 남겨주시면, 답변 드리겠습니다.

★ 읽어주셔서 감사합니다★

 

728x90
반응형
728x90
반응형

 

728x90

안녕하세요!

최근에 크롬 드라이버를 설치하는 페이지가 변경되어 새롭게 포스팅을 하겠습니다!

 

이전 포스팅이 궁금하신 분들은 아래 포스팅을 참고해주시면 됩니다!

https://yhj9855.com/entry/%ED%81%AC%EB%A1%AC%EB%93%9C%EB%9D%BC%EC%9D%B4%EB%B2%84ChromeDriver-%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0

 

크롬드라이버(ChromeDriver) 설치하기

안녕하세요! 요즘 너무 바빠서 자주 글을 올리지 못하고 있네요ㅠㅠ 오늘은 동적 크롤링에 꼭 필요한 크롬드라이버(ChromeDriver)를 설치하는 방법에 대해서 포스팅 하겠습니다. 크롬드라이버란?

yhj9855.com

  • 크롬드라이버란?

컴퓨터가 크롬 브라우저를 제어할 수 있도록 해주는 프로그램입니다.

  • 크롬드라이버의 사용처

크롬드라이버는 컴퓨터 코드 크롬 브라우저에서 특정 행동을 하고 싶을 때 사용합니다.

※ 특정 행동은 스크롤을 내리거나, 버튼을 누르는 등의 행동을 예시로 들 수 있습니다.

크롬 브라우저를 직접 제어할 수 있기 때문에 URL이 변경되지 않는데 내용은 변경이 되는 동적 페이지 크롤링에 필수로 사용됩니다.

  • 크롬드라이버 설치하기

크롬드라이버는 크롬 버전과 버전이 맞지 않으면 코드에서 오류가 발생하기 때문에 꼭 동일한 버전의 크롬드라이버를 설치하는 것이 중요합니다.

크롬드라이버를 사용하는 selenium 패키지가 업데이트 되면서 크롬드라이버를 새롭게 설치할 필요가 없다고들 하는데, 저는 자꾸 오류가 발생해서 번거롭지만 버전에 맞는 크롬드라이버를 매번 다시 설치해주고 있습니다ㅠㅠ

크롬드라이버를 설치하는 과정은 아래와 같습니다.

 

1. 크롬을 최신 버전으로 업데이트 해줍니다.

최신 버전의 크롬이 가장 오류가 나지 않기 때문에 크롬드라이버를 설치하기 전에 크롬을 꼭 최신 버전으로 업데이트 해주세요!

 

2. 크롬의 버전을 확인합니다.

'제어' → '도움말' → 'Chrome 정보' 에서 크롬 버전을 확인할 수 있습니다.

여기서 제어는 크롬 내 X(창닫기 버튼) 아래에 있는 세로로 된 ... 을 의미합니다.

 

3. https://chromedriver.chromium.org/downloads 홈페이지에 접속합니다.

 

다운로드  |  ChromeDriver  |  Chrome for Developers

이 페이지는 Cloud Translation API를 통해 번역되었습니다. 다운로드 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 달리 명시되지 않는 한 이 페이지의 콘텐츠

developer.chrome.com

구글에서 크롬드라이버를 검색했을 때, 가장 상단에 보이는 홈페이지와 동일한 홈페이지입니다!

 

4. 아래 이미지에 해당하는 부분을 클릭해줍니다.

메인 페이지가 변경되면서 다운로드 파일이 있는 곳으로 가는 버튼이 달라졌습니다!

현재 사용하시는 크롬은 무조건 115버전 보다 상위 버전이기 때문에 해당 부분을 클릭하여 최신 버전 다운로드 페이지로 이동해주세요!

 

5. 'Stable' 에서 자신에게 맞는 OS를 선택하여 다운로드를 받아줍니다.

다운로드는 URL을 복사하여 크롬 주소창에 입력해주면 자동으로 다운로드 진행이 됩니다.

자신에게 맞는 OS는 컴퓨터 내 '설정'  → '시스템'  → '시스템 종류'에서 확인 하실 수 있습니다!

※Stable에서 제공하는 크롬드라이버와 본인의 크롬 버전이 맞지 않을 경우

종종 크롬 업데이트가 잘 반영 되지 않아서, 현재 크롬 버전과 Stable에서 제공하는 크롬 버전이 다른 경우가 있습니다!

Stable에서 제공하는 크롬 버전은 아래 이미지처럼 확인이 가능합니다.

128.0.n.n 을 사용하시는 분들은 해당 버전이 맞지만, 127.n.n.n을 사용하시는 분들은 해당 버전이 맞지 않는데요!

이럴 경우에는 직접 검색을 하셔서 자신에게 맞는 크롬 드라이버 다운 링크를 찾으셔야 합니다.

 

1)  https://chromedriver.chromium.org/downloads 홈페이지에 다시 접속해주세요

뒤로 가기를 통해 메인 화면으로 돌아가셔도 됩니다!

 

2) 아래 이미지에 해당하는 부분을 클릭해줍니다.

 

3) 본인의 크롬 버전을 검색합니다.

검색은 Ctrl+F를 누르면 검색어를 입력할 수 있는 작은 창이 뜹니다.

해당 창에 자신의 크롬 버전을 검색해주시면 됩니다!

자신의 크롬 버전이 나타나지 않을 경우, 크롬 정보에서 가장 마지막 '.' 뒤를 제거하여 검색해주시면 됩니다!

예시) 크롬 버전: 127.0.6533.122 → 127.0.6533 을 검색

 

4) 본인의 크롬 버전과 동일하고 환경에 맞는 크롬 드라이버 다운로드 링크를 찾으시면 됩니다.

가장 마지막 '.' 뒤의 숫자는 달라도 같은 버전입니다!

예시) 127.0.6533.122의 크롬일 경우, 127.0.6533까지 동일한 파일이면 모두 호환 가능합니다.

 

다운로드 링크 찾는 방법

[1] chromedriver라는 값을 찾습니다.

 

[2] platform에서 자신에게 맞는 환경을 찾습니다.

저는 윈도우 64이기 때문에 해당 환경을 찾았습니다!

 

[3] url 값을 복사하여 주소창에 입력해줍니다.

자동으로 다운로드 되는 것을 확인하시면, 크롬 드라이버 다운로드가 잘 된 것입니다!

 

6. 설치 완료된 크롬드라이버 zip 파일에서 'chromedriver.exe' 파일만 복사하여, 코드가 있는 폴더에 옮겨줍니다.

같은 폴더에 있지 않으면, 코드가 인식하지 못하는 경우가 있어서 옮겨주는 것이 좋습니다.

 

이 과정을 통해서 최신 버전의 크롬드라이버를 설치할 수 있습니다!

크롬드라이버를 사용하여 동적 크롤링을 진행하는 포스팅은 다음에 진행하도록 하겠습니다.

 

궁금한 부분이 있으신 분들은 댓글로 남겨주시면, 답변 드리도록 하겠습니다.

★읽어주셔서 감사합니다★

 

728x90
반응형
728x90
반응형

안녕하세요! 오늘은 원신 나무위키에 플레이어블 캐릭터성유물 카테고리의 글을 크롤링하는 코드에 대해 포스팅 진행하겠습니다.

해당 포스팅에서는 전체 코드와 결과물 이미지만 첨부합니다.

크롤링의 자세한 과정은 추후에 포스팅 진행하도록 하겠습니다.

 

해당 크롤링은 원신 각 캐릭터의 성유물 추천 옵션과 세트를 빠르게 파악하기 위한 데이터 수집을 목적으로 하고 있습니다!

나무위키에서 수집할 정보는 아래와 같습니다.

※ 사진 속 정보는 나히다를 예시로 한 것입니다.

1. 캐릭터의 이름, 속성, 무기

 

2. 권장 성유물 옵션

 

3. 추천 성유물 세트 및 설명

 

4. 성유물 이름, 세트 효과, 획득처

 

  • 크롤링 진행 방식

크롤링은 총 3개의 코드로 진행을 합니다.

 

  • 첫 번째 코드
  1. 원신 캐릭터의 상세 정보가 담긴 링크를 전부 긁어옵니다.
  2. 캐릭터의 이름과 링크만 저장하여 하나의 엑셀 파일로 저장합니다.
  • 두 번째 코드
  1. 첫 번째 코드에서 저장한 엑셀 파일에서 각 캐릭터의 상세 정보 링크를 가져옵니다.
  2. 캐릭터의 속성, 무기, 권장 성유물 옵션, 추천 성유물 세트 및 상세 설명의 내용을 가지고 옵니다.
  3. 캐릭터의 이름, 속성, 무기 권장 성유물 옵션을 저장하여 하나의 엑셀 파일로 저장합니다.
  4. 캐릭터의 이름, 추천 성유물 세트, 상세 설명을 저장하여 하나의 엑셀 파일로 저장합니다.

엑셀 파일을 두 개로 나눈 이유는 이후에 원신 캐릭터 성유물을 조회하는 엑셀 파일을 쉽게 만들기 위해서 입니다!

  • 세번째 코드
  1. 성유물 세트 이름, 2세트, 4세트, 획득처의 내용을 가지고 옵니다.
  2. 획득처에서 비경의 이름을 분리합니다.
  3. 성유물 세트 이름, 2세트, 4세트, 획득처, 비경의 이름을 저장하여 하나의 엑셀 파일로 저장합니다.

비경 이름을 분리한 이유는 이후 원신 캐릭터 성유물을 조회하는 엑셀 파일을 쉽게 만들기 위해서 입니다!

 

[실제 코드 및 결과물]

  • 첫 번째 코드
import pandas as pd
from selenium import webdriver
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as E
from openpyxl import *

# 원신/캐릭터 나무위키 링크
link = 'https://namu.wiki/w/%EC%9B%90%EC%8B%A0/%EC%BA%90%EB%A6%AD%ED%84%B0'

# 나무위키는 BeautifulSoup이 먹히지 않기 때문에 동적 크롤링으로 진행
driver = webdriver.Chrome('chromedriver.exe')
driver.get(link)
time.sleep(3)

# '원소별' 버튼 클릭
button = list(driver.find_elements(By.CLASS_NAME, "_3xTXXXtF"))
button[1].click()
time.sleep(3)

Character = pd.DataFrame({'캐릭터 이름' : [], '링크' : []})

character_info = driver.find_elements(By.CLASS_NAME, "s3zppxXT")

for i in range(len(character_info)) :
    character = character_info[i]
    # 캐릭터 이름만 담기 위해서 데이터를 정제하는 부분
    # 캐릭터의 소개가 끝나는 부분
    if character.text == '취소선' :
        break
    # 주인공 캐릭터인 아이테르와 루미네는 데이터 수집에서 제외
    # 캐릭터 이름이 아닌데, 들어온 정보는 모두 제외
    if character.text == '' or  '원신' in character.text or '아이테르' in character.text or character.text in ['불', '물', '바람', '번개', '풀', '얼음', '바위'] :
        pass
    else :
    	# 캐릭터의 이름
        name = character.text
        # 캐릭터의 이름이 길 경우, 엔터로 구분이 되어있기 때문에 이를 띄어쓰기로 변경
        if '\n' in name :
            name = name.replace('\n', ' ')
        Char = [name, str(character.get_attribute('href'))]
        Character.loc[i] = Char

with pd.ExcelWriter('genshin_link.xlsx') as writer :
    Character.to_excel(writer, sheet_name='링크', index=False)

 

  • 첫 번째 코드 결과물

 

  • 두 번째 코드
import pandas as pd
from selenium import webdriver
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as E
from openpyxl import *

# 캐릭터의 이름과 상세정보 링크가 담긴 엑셀 파일
link = pd.read_excel('genshin_link.xlsx')
Main_link = list(link['링크'])
Character = list(link['캐릭터 이름'])

Information = pd.DataFrame({'캐릭터 이름' : [], '무기' : [], '시간의 모래' : [], '공간의 성배' : [], '이성의 왕관' : [], '부옵션' : []})
Relic_Information = pd.DataFrame({'캐릭터 이름' : [], '성유물' : [], '평가': []})

driver = webdriver.Chrome('chromedriver.exe')

# 엑셀 전체 인덱스를 의미
# 저장할 엑셀이 두 개이기 때문에 인덱스도 두 개가 필요
total_index = 0
relic_index = 0

# 특정 캐릭터의 상세정보에서 오류가 발생할 경우을 대비
try :
    for k in range(len(Main_link)) :
        driver.get(Main_link[k])
        time.sleep(3)
        
        # 캐릭터의 무기 수집 과정
        attack = driver.find_elements(By.CLASS_NAME, 'cIflhYhI')
        for i in range(len(attack)) :
            if '무기' == attack[i].text :
                attack_index = i+1
                break
        weapon = attack[attack_index].text
        
        # 캐릭터의 성유물 수집 과정
        info = driver.find_elements(By.CLASS_NAME, 'D7SMSdcV')
        for i in range(len(info)) :
        	# 권장 성유물 옵션을 파악하기 위해 위치를 저장
            if '권장 성유물' in info[i].text :
                index = i
                break
        # 권성유물의 정보가 담긴 공간
        sung = info[index]
        
        # 권장 성유물 옵션 수집 과정
        options = sung.find_elements(By.CLASS_NAME, 'cIflhYhI')
        Option = [Character[k], weapon]
        for j in range(len(options)) :
        	# 권장 성유물 옵션에서 필요한 정보가 들어있는 부분
            if j in [4, 5, 6, 8] :
                option = options[j].text
                # 옵션이 여러 개일 경우, 줄바꿈으로 구분하기 때문에 이를 / 구분으로 변경
                if '\n' in option :
                    option = option.replace('\n', ' / ')
                Option.append(option)
        # 권장 성유물의 옵션만 담는 부분
        Information.loc[total_index] = Option
        total_index = total_index+1
        
        # 추천 성유물 세트 및 상세 설명 수집 과정
        sets = sung.find_elements(By.CLASS_NAME, 'W078FM6Z')
        # 성유물 세트는 캐릭터마다 여러 개 존재하기 때문에 이를 구분하기 위한 부분
        character_number = 1
        for j in range(len(sets)) :
            # li로 구분되어 있는데, 그 안에 div가 같이 들어가 있기 때문에 문제가 발생한다.
            relic_info = list(sets[j].text.split('\n'))
            for m in range(len(relic_info)) :
            	# 실제 정보가 들어가 있는 부분
                if m%2 == 1:
                    one_set = relic_info[m-1]
                    set_info =relic_info[m]
                    character_name = Character[k]+'%d' %(character_number)
                    Option = [character_name, one_set, set_info]
                    Relic_Information.loc[relic_index] = Option
                    character_number = character_number+1
                    relic_index = relic_index+1
except Exception as e :
    print(e)
    print(Main_link[k])
        
with pd.ExcelWriter('genshin.xlsx') as writer :
    Information.to_excel(writer, sheet_name='성유물 옵션', index=False)

with pd.ExcelWriter('genshin_set_relic.xlsx') as writer :
    Relic_Information.to_excel(writer, sheet_name='성유물', index=False)

 

  • 두 번째 코드 결과물

 

  • 세 번째 코드
import pandas as pd
from selenium import webdriver
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as E
from openpyxl import *

# 원신/성유물 나무위키 링크
link = 'https://namu.wiki/w/%EC%9B%90%EC%8B%A0/%EC%84%B1%EC%9C%A0%EB%AC%BC'

driver = webdriver.Chrome('chromedriver.exe')
driver.get(link)
time.sleep(3)

Relic = pd.DataFrame({'성유물 세트' : [], '2세트' : [], '4세트' : [], '획득처' : [], '비경' : []})
total_index = 0

# 성유물 세트 효과 수집 과정
info = driver.find_elements(By.CLASS_NAME, 'TiHaw-AK._6803dcde6a09ae387f9994555e73dfd7')
for i in range(len(info)) :
    # 첫 번째 성유물이 검투사의 피날레이기 때문에 해당 부분이 기준
    # 여기서부터 끝까지가 성유물에 대한 정보가 존재
    if '검투사' in info[i].text :
        index_start = i
        break
# 전체적으로 3단위로 원하는 정보가 있음
for i in range(index_start, len(info), 3) :
    relic_info = info[i].text.split('\n')
    # 1세트 효과가 있는 4성 성유물은 생략한다.
    if '모시는 자' in relic_info[0] :
        continue
    # 획득처에서 비경을 구분하는 과정
    if '비경' in  relic_info[7] :
        place_index_start = relic_info[7].index(':')
        place = relic_info[7][place_index_start+2:]
        if ',' in place :
            place_end_index = place.index(',')
            place = place[:place_end_index]
    else :
        place = ''
    relic = [relic_info[0], relic_info[3], relic_info[5], relic_info[7], place]
    Relic.loc[total_index] = relic
    total_index = total_index+1

with pd.ExcelWriter('genshin_relic.xlsx') as writer :
    Relic.to_excel(writer, sheet_name='성유물', index=False)

 

  • 세 번째 코드 결과물

 

해당 데이터를 활용하여 원신 캐릭터의 성유물을 엑셀에서 쉽게 조회하는 포스팅은 아래에서 확인해주세요!

https://yhj9855.com/entry/%EC%9B%90%EC%8B%A0-%EC%84%B1%EC%9C%A0%EB%AC%BC-%EC%84%B8%ED%8C%85-%EC%97%91%EC%85%80%EB%A1%9C-%EC%89%BD%EA%B2%8C-%EB%B3%B4%EA%B8%B0-45-%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8-%EA%B8%B0%EC%A4%80-%EC%B9%98%EC%98%A4%EB%A6%AC-%ED%8F%AC%ED%95%A8

 

[원신] 성유물 세팅 엑셀로 쉽게 보기 (4.5 업데이트 기준, 치오리 포함)

안녕하세요! 오늘은 원신 4.5 업데이트 모든 캐릭터 성유물 세팅 포스팅을 진행하겠습니다. 4.5 업데이트는 간단한 업데이트여서 호다닥 갖고 왔습니다. 캐릭터의 성유물 세팅은 엑셀 내 저장되

yhj9855.com

 

코드에 대해 궁금한 부분이 있으신 분들은 댓글로 남겨주시면, 답변 드리도록 하겠습니다!

★읽어주셔서 감사합니다★

728x90
반응형
728x90
반응형

728x90

안녕하세요! 네이버 뉴스가 24년 1월 25일부터 페이지가 보여주는 방식이 변경되면서, 이전 포스팅에서 진행했던 첫 번째 코드를 사용할 수 없게 되었습니다.

 

그래서 변경된 페이지에서 적용되는 크롤링 코드를 새로 가지고 왔습니다!

※ 두 번째 코드는 동일하게 적용됩니다.

 

해당 포스팅에서는 전체 코드만 첨부합니다.

크롤링할 페이지의 설명, 크롤링 진행 방식, 이전 크롤링 코드가 궁금하신 분들은 아래 링크에서 확인하실 수 있습니다.

https://yhj9855.com/entry/Crawling-%EB%84%A4%EC%9D%B4%EB%B2%84-%EB%89%B4%EC%8A%A4-%ED%81%AC%EB%A1%A4%EB%A7%81-1

 

[Crawling] 네이버 뉴스 크롤링 - 1

안녕하세요. 크롤링에서 가장 첫 포스팅을 네이버 뉴스 크롤링으로 하게 되었어요. 아무래도 바쁜 일상 속에서 매일 뉴스 기사를 파악하는 부분이 시간적으로 힘들었는데, 크롤링하고 데이터

yhj9855.com

 

크롤링의 자세한 과정이 궁금하신 분들은 아래 링크를 봐주시면 됩니다!

 

<첫 번째 코드의 자세한 과정>

https://yhj9855.com/entry/Crawling-%EB%84%A4%EC%9D%B4%EB%B2%84-%EB%89%B4%EC%8A%A4-%ED%81%AC%EB%A1%A4%EB%A7%81-2-%EB%B3%80%EA%B2%BD

 

[Crawling] 네이버 뉴스 크롤링 - 2 (변경)

안녕하세요. 오늘은 기존에 작성한 네이버 뉴스 클로링 코드에서 첫 번째 코드의 자세한 크롤링 과정을 포스팅 하겠습니다.새롭게 변경된 첫 번째 코드의 자세한 크롤링 과정입니다! 네이버 뉴

yhj9855.com

 

 

<두 번째 코드의 자세한 과정>

https://yhj9855.com/entry/Crawling-%EB%84%A4%EC%9D%B4%EB%B2%84-%EB%89%B4%EC%8A%A4-%ED%81%AC%EB%A1%A4%EB%A7%81-3

 

[Crawling] 네이버 뉴스 크롤링 - 3

안녕하세요. 오늘은 기존에 작성한 네이버 뉴스 크롤링 코드에서 두 번째 코드의 자세한 크롤링 과정을 포스팅 하겠습니다. 네이버 뉴스 크롤링 전체 코드를 확인하고 싶으신 분들은 아래 링크

yhj9855.com


[첫 번째 코드]

전체 코드

import pandas as pd
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
import time
from selenium.webdriver.common.by import By
from openpyxl import *

# 2024.01.25 부터 변경된 네이버 기사를 새로 크롤링하기 위해 만든 코드

link = 'https://news.naver.com/breakingnews/section/105/229?date='
# 스크랩 하고 싶은 날짜를 년도월일 나열해준다.
# 날짜를 쉽게 바꾸기 위해 date를 따로 선언해준다.
date = '20250107'
# 메인 링크는 링크에 날짜가 붙은 구조이기 때문에 이렇게 작성해준다.
main_link = link + date 
Main_link = pd.DataFrame({'number' : [], 'title' : [], 'link' : []})

# Selenium 4 버전 이상 부터는 해당 방법으로 사용해야 driver 인식이 된다.
service = Service('chromedriver.exe')
driver = webdriver.Chrome(service=service)
driver.get(main_link)
time.sleep(3)

# 기사 더보기 버튼
more_button = driver.find_element(By.CLASS_NAME, 'section_more_inner._CONTENT_LIST_LOAD_MORE_BUTTON')

# 기사 더보기가 몇 개가 있을지 모르기 때문에 오류가 날 때까지 누르는 것으로 한다.
# 여기서 발생하는 오류란 버튼을 찾을 수 없다 즉, 버튼이 없을 때 발생하는 오류이다.
while True :
    try :
        more_button.click()
        time.sleep(3)
    except :
        break

articles = driver.find_elements(By.CLASS_NAME, 'sa_text_title._NLOG_IMPRESSION')
for i in range(len(articles)) :
    title = articles[i].text.strip()
    link = articles[i].get_attribute('href')
    li = [i+1, title, link]
    Main_link.loc[i] = li


excel_name = 'news_' + date + '.xlsx'
with pd.ExcelWriter(excel_name) as writer :
    Main_link.to_excel(writer, sheet_name='링크', index=False)

[두 번째 코드]

전체 코드

from bs4 import BeautifulSoup
import requests
import pandas as pd
from openpyxl import *
import time
import urllib

# 첫 번째 코드에서 지정한 뉴스의 링크들이 담긴 파일
link = pd.read_excel('news_20231222.xlsx')
# 엑셀 파일이 헷갈리지 않게 최종 결과파일에도 날짜를 넣어줌
excel_name = 'news_detail_20231222.xlsx'
Main_link = list(link['link'])
# number: 기사의 수, title: 기사의 제목, information: 본문 내용, link: 기사의 링크
Information = pd.DataFrame({'number' : [], 'title' : [], 'information' : [], 'link' : []})
# 본문 내용만 추가하면 되기 때문에 데이터 프레임에 미리 나머지 내용을 담아줌
Information['number'] = link['number']
Information['title'] = link['title']
Information['link'] = link['link']
information = []

for main_link in Main_link :
	# 기사가 전체적으로 2개의 구조를 가지고 있음 (게임/리뷰 카테고리에 한하여)
    # 하나의 구조를 기준으로 삼고, 해당 부분에서 오류가 발생하면 다음 구조의 기사로 판단
    try :
        response = requests.get(main_link, headers={'User-Agent':'Moailla/5.0'})
        if response.status_code == 200 :
            html = response.content
            soup = BeautifulSoup(html, 'html.parser')
            # 기사의 본문 내용만 담고 있는 부분
            info = soup.find('div', {'id' : 'newsct_article'}).text.strip()
            # 기사 내용 데이터 분석을 위해서 줄바꿈을 띄어쓰기로 변경
            info = info.replace('\n', '')
            information.append(info)
    except :
    	# 다른 구조의 기사 크롤링 코드
        # 여기서 오류가 나는 경우는 게임/리뷰 기사가 아닌 다른 카테고리의 기사로 판단
        try :
            response = requests.get(main_link, headers={'User-Agent':'Moailla/5.0'})
            if response.status_code == 200 :
                html = response.content
                soup = BeautifulSoup(html, 'html.parser')
                # 기사의 본문 내용을 담고 있는 부분
                info = soup.find('div', {'id' : 'newsEndContents'}).text.strip()
                info = info.replace('\n', '')
                # 해당 구조의 기사는 기자의 정보가 본문과 무조건 같이 존재
                # 기자의 정보 부분은 필요가 없기 때문에 기자 정보의 기준점이 되는 부분을 찾음
                # 기자의 정보 기준이 기사제공이라는 단어이기 때문에 그 이후는 삭제
                end = info.index('기사제공')
                info = info[:end]
                information.append(info)
        # 다른 카테고리의 기사가 들어올 경우에는 정보를 담지 않는 것으로 함
        except Exception as e :
        	info = ''
            information.append(info)
            # 오류가 발생하는 이유와 발생하는 링크를 출력하여 오류를 확인하는 장치
            #print(e)
            #print(main_link)

Information['information'] = information

with pd.ExcelWriter(excel_name) as writer :
    Information.to_excel(writer, sheet_name='결과값', index=False)

 

뉴스 크롤링 데이터를 이용한 워드클라우드 포스팅은 아래에서 확인해주세요!

https://yhj9855.com/entry/%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B6%84%EC%84%9D-%ED%95%9C%EA%B8%80%EB%A1%9C-%EC%9B%8C%EB%93%9C%ED%81%B4%EB%9D%BC%EC%9A%B0%EB%93%9C-%EB%A7%8C%EB%93%A4%EA%B8%B0-feat%EB%84%A4%EC%9D%B4%EB%B2%84-%EB%89%B4%EC%8A%A4-%ED%81%AC%EB%A1%A4%EB%A7%81

 

[데이터 분석] 한글로 워드클라우드 만들기 (feat.네이버 뉴스 크롤링)

안녕하세요. 오늘은 크롤링 데이터로 워드클라우드(wordcloud)를 만드는 방법에 대해 포스팅 하겠습니다. 크롤링 데이터는 네이버 뉴스 크롤링을 사용할 예정입니다! 네이버 뉴스 크롤링 과정이

yhj9855.com

 

뉴스 크롤링 데이터를 이용한 토픽모델링 포스팅은 아래세어 확인해주세요!

https://yhj9855.com/entry/%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B6%84%EC%84%9D-%ED%95%9C%EA%B8%80-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%86%A0%ED%94%BD-%EB%AA%A8%EB%8D%B8%EB%A7%81-%EC%A7%84%ED%96%89%ED%95%98%EA%B8%B0

 

[데이터 분석] 한글 데이터 토픽 모델링 진행하기

안녕하세요! 오늘은 한글 데이터로 토픽 모델링(topic modeling)을 하는 방법에 대해 포스팅 하겠습니다. 한글 데이터는 네이버 뉴스 크롤링 데이터를 사용할 예정입니다. 네이버 뉴스 크롤링 과정

yhj9855.com

코드에 대해 궁금한 부분이 있으신 분들은 댓글로 남겨주시면, 답변 드리도록 하겠습니다.

★읽어주셔서 감사합니다★

 

728x90
반응형
728x90
반응형

안녕하세요!

오늘은 한국어 데이터 분석을 위해 꼭 필요한 Konlpy(코엔엘파이) 라이브러리에 대해서 포스팅 하겠습니다.

 

  • Konlpy 란?

한국어 자연어 처리를 위한 파이썬 라이브러리 입니다.

 

  • Konlpy의 특징

Konlpy은 다양한 한국어 형태소 분석기를 지원하기 때문에 같은 문장을 다른 방식으로 분석을 하는 것이 가능합니다.

※ Konlpy는 설치 및 사용 환경을 설정하는 과정이 조금 복잡하기 때문에 따로 포스팅을 진행하도록 하겠습니다!

 

  • Konlpy이 제공하는 형태소 분석기 종류

Konlpy은 아래 5가지 형태소 분석기를 제공합니다.

  1. Kkma
  2. Komoran
  3. Hannaum
  4. Okt (구 Twittrer)
  5. Mecab: 일본어 형태소 분석기를 한국어로 변경한 분석기로, 세종 말뭉치로 만들어진 CSV 형태의 사전 (Windows에서 잘 지원이 되지 않음)

Mecab은 Windows에서 잘 지원이 되지 않기 때문에 나머지 4개의 형태소 분석기만 비교를 해보도록 하겠습니다!

728x90

 

 

  • Kkma

서울대 지능형 데이터 시스템 연구실에서 개발한 형태소 분석기 입니다.

비교적 정확한 품사를 구분하는 형태소 분석기로 띄어쓰기 오류에 강하고, 다의어 처리에 효과적이지만, 단어의 수가 증가할수록 분석을 진행하는 시간이 가장 크게 느려지는게 특징입니다.

Kkma는 4가지의 함수를 제공합니다.

아래는 4가지 함수와 코드 예시입니다.

from konlpy.tag import Kkma

kkma = Kkma()
text = "꼬꼬마 형태소 분석기를 사용해 봅시다."

 

  • morphs: 문장을 형태소 단위로 추출
morphs_result = kkma.morphs(text)

Output: ['꼬꼬마', '형태소', '분석', '기', '를', '사용', '하', '어', '보', 'ㅂ시다', '.']

 

  • nouns: 문장을 명사 단위로 추출
nouns_result = kkma.nouns(text)

Output: ['꼬꼬마', '형태소', '분석', '분석기']

 

  • pos: 문장을 형태소 단위로 추출, 각 형태소의 품사와 함께 반환
pos_result = kkma.pos(text)

Output: [('꼬꼬마', 'NNG'), ('형태소', 'NNG'), ('분석', 'NNG'), ('기', 'NNG'), ('를', 'JKO'), ('사용', 'NNG'), ('하', 'XSV'), ('어', 'ECS'), ('보', 'VXV'), ('ㅂ시다', 'EFN'), ('.', 'SF')]

 

  • sentences : 여러 문장으로 이루어진 텍스트를 문장 단위로 추출
text = "꼬꼬마 형태소 분석기를 사용해 봅시다. 이것은 간단한 예시입니다."
sentences_result = kkma.sentences(text)

Output: ['꼬꼬마 형태소 분석기를 사용해 봅시다.', '이것은 간단한 예시입니다.']

 

 

  • Komoran

Shineware에서 개발한 형태소 분석기 입니다.

대용량 말뭉치를 학습하여 품사 태깅에 높은 정확도를 보이며, 빠른 분석 속도높은 성능을 보유하고 있습니다.

Komoran은 3가지의 함수를 제공합니다.

아래는 3가지 함수와 코드 예시입니다.

from konlpy.tag import Komoran

komoran = Komoran()
text = "코모란 형태소 분석기를 사용해 봅시다."

 

  • morphs: 문장을 형태소 단위로 추출
morphs_result = komoran.morphs(text)

Output: ['코모란', '형태소', '분석', '기', '를', '사용', '하', '아', '보', 'ㅂ시다', '.']

 

  • nouns: 문장을 명사 단위로 추출
nouns_result = komoran.nouns(text)

Output: ['코모란', '형태소', '분석', '기']

 

  • pos: 문장을 형태소 단위로 추출, 각 형태소의 품사와 함께 반환
pos_result = komoran.pos(text)

Output: [('코모란', 'NNP'), ('형태소', 'NNG'), ('분석', 'NNG'), ('기', 'NNG'), ('를', 'JKO'), ('사용', 'NNG'), ('하', 'XSV'), ('아', 'EC'), ('보', 'VX'), ('ㅂ시다', 'EF'), ('.', 'SF')]

 

 

  • Hannaum

국립 국어원에서 개발한 형태소 분석기 입니다.

복합 명사를 적절하게 처리하고, 복잡한 구조의 문장에서도 안정적으로 형태소 분석을 수행합니다.

Hannaum은 3가지의 함수를 제공합니다.

아래는 3가지 함수와 코드 예시입니다.

from konlpy.tag import Hannanum

hannanum = Hannanum()
text = "한나눔 형태소 분석기를 사용해 봅시다."

 

  • morphs: 문장을 형태소 단위로 추출
morphs_result = hannanum.morphs(text)

Output: ['한나눔', '형태소', '분석', '기', '를', '사용', '하', '어', '보', 'ㅂ시다', '.']

 

  • nouns: 문장을 명사 단위로 추출
nouns_result = hannanum.nouns(text)

Output: ['한나눔', '형태소', '분석', '기']

 

  • pos: 문장을 형태소 단위로 추출, 각 형태소의 품사와 함께 반환
pos_result = hannanum.pos(text)

Output: [('한나눔', 'N'), ('형태소', 'N'), ('분석', 'N'), ('기', 'N'), ('를', 'J'), ('사용', 'N'), ('하', 'X'), ('어', 'E'), ('보', 'P'), ('ㅂ시다', 'E'), ('.', 'S')]

 

 

  • Okt

Twitter에서 개발한 형태소 분석기 입니다.

복합 명사 분석에 우수하며, 속도가 빠르고 경량화된 형태로 제공되어 다양한 환경에서 사용할 수 있습니다.

Okt는 4가지의 함수를 제공합니다.

아래는 4가지 함수와 코드 예시입니다.

from konlpy.tag import Okt

okt = Okt()
text = "Okt 형태소 분석기를 사용해 봅시다."

 

  • morphs: 문장을 형태소 단위로 추출
morphs_result = okt.morphs(text)

Output: ['Okt', '형태소', '분석기', '를', '사용', '해', '봅시다', '.']

 

  • nouns: 문장을 명사 단위로 추출
nouns_result = okt.nouns(text)

Output: ['Okt', '형태소', '분석기']

 

  • pos: 문장을 형태소 단위로 추출, 각 형태소의 품사와 함께 반환
pos_result = okt.pos(text)

Output: [('Okt', 'Alpha'), ('형태소', 'Noun'), ('분석기', 'Noun'), ('를', 'Josa'), ('사용', 'Noun'), ('해', 'Verb'), ('봅시다', 'Verb'), ('.', 'Punctuation')]

 

  • phrases: 문장을 어구 단위로 추출
phrases_result = okt.phrases(text)

Output: ['Okt', 'Okt 형태소', '형태소', '분석기', '사용', 'Okt 형태소 분석기', '분석']

 

 

Konlpy를 활용하여 한국어 데이터 기반 워드 클라우드, 토픽 모델링 데이터 분석을 진행한 예시는 아래 페이지에서 확인하실 수 있습니다!

https://yhj9855.com/entry/%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B6%84%EC%84%9D-%ED%95%9C%EA%B8%80%EB%A1%9C-%EC%9B%8C%EB%93%9C%ED%81%B4%EB%9D%BC%EC%9A%B0%EB%93%9C-%EB%A7%8C%EB%93%A4%EA%B8%B0-feat%EB%84%A4%EC%9D%B4%EB%B2%84-%EB%89%B4%EC%8A%A4-%ED%81%AC%EB%A1%A4%EB%A7%81

 

[데이터 분석] 한글로 워드클라우드 만들기 (feat.네이버 뉴스 크롤링)

안녕하세요. 오늘은 크롤링 데이터로 워드클라우드(wordcloud)를 만드는 방법에 대해 포스팅 하겠습니다. 크롤링 데이터는 네이버 뉴스 크롤링을 사용할 예정입니다! 네이버 뉴스 크롤링 과정이

yhj9855.com

 

https://yhj9855.com/entry/%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B6%84%EC%84%9D-%ED%95%9C%EA%B8%80-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%86%A0%ED%94%BD-%EB%AA%A8%EB%8D%B8%EB%A7%81-%EC%A7%84%ED%96%89%ED%95%98%EA%B8%B0

 

[데이터 분석] 한글 데이터 토픽 모델링 진행하기

안녕하세요! 오늘은 한글 데이터로 토픽 모델링(topic modeling)을 하는 방법에 대해 포스팅 하겠습니다. 한글 데이터는 네이버 뉴스 크롤링 데이터를 사용할 예정입니다. 네이버 뉴스 크롤링 과정

yhj9855.com

 

궁금한 부분이 있으신 분들은 댓글로 남겨주시면, 답변 드리도록 하겠습니다!

★읽어주셔서 감사합니다★

728x90
반응형

+ Recent posts