평소 Linux기반의 서버만 사용해왔는데 Windows Sever를 구축할 일이 생겼습니다.

ftp를 설정해주려했는데 ssh만 설정해주면 ftp도 자동으로 설정되는 것이어서, windows 답게 예상보다 간단하게 설정할 수 있었습니다.

 


Windows PowerShell에서 진행합니다.

MS사이트에 자세하게 나와있어서 아래 링크대로만 수행하셔도 큰 문제는 없을 겁니다.

(https://learn.microsoft.com/ko-kr/windows-server/administration/openssh/openssh_install_firstuse?tabs=powershell)

 

 

Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.
                                                                                                                 새로운 기능 및 개선 사항에 대 한 최신 PowerShell을 설치 하세요! https://aka.ms/PSWindows                                                                                                                                          PS C:\Users\Administrator> Get-WindowsCapability -Online | ? Name -like 'OpenSSH*'
Name  : OpenSSH.Client~~~~0.0.1.0
State : Installed

Name  : OpenSSH.Server~~~~0.0.1.0
State : NotPresent



PS C:\Users\Administrator> Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0


Path          :
Online        : True
RestartNeeded : False



PS C:\Users\Administrator> Get-WindowsCapability -Online | ? Name -like 'OpenSSH*'


Name  : OpenSSH.Client~~~~0.0.1.0
State : Installed

Name  : OpenSSH.Server~~~~0.0.1.0
State : Installed



PS C:\Users\Administrator> Start-Service sshd
PS C:\Users\Administrator> Set-Service -Name sshd -StartupType 'Automatic'
PS C:\Users\Administrator> if (!(Get-NetFirewallRule -Name "OpenSSH-Server-In-TCP" -ErrorAction SilentlyContinue | Select-Object Name, Enabled)) {
>>     Write-Output "Firewall Rule 'OpenSSH-Server-In-TCP' does not exist, creating it..."
>>     New-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22
>> } else {
>>     Write-Output "Firewall rule 'OpenSSH-Server-In-TCP' has been created and exists."
>> }
Firewall rule 'OpenSSH-Server-In-TCP' has been created and exists.

 

 

감사합니다.

MacOS에서 Windows Server OS를 탑재한 서버의 폴더에 접근해보겠습니다.

먼저 윈도우 서버에 SSH, FTP가 설정되어 있을 때 입니다.

그리고 당연히 접근할 서버에 접근이 가능한 네트워크 상태여야합니다.


Finder  실행

Finder 실행

단축키 ⌘ + K 를 누른 후 'smb://{호스트 IP}'를 입력합니다

 

마운트 할 볼륨(폴더 경로 선택) 합니다

그럼 Finder에서 마운트된 볼륨(FTP 폴더경로)에 접근하실 수 있습니다.

간단합니다.


그렇다면 smb란 무엇일까요?

sftp, ftp 프로토콜이 아닌 'smb'로 호스트를 마운트합니다.

아래 나무위키에 쉽게 설명이 되어있어서 발췌를 하였습니다. (https://namu.wiki/w/SMB)

 

결론적으로 같은 네트워크 상에 있는 다른 OS(컴퓨터)간에 파일, 자원등을 공유할 수 있는 프로토콜입니다.

Server Message Block.

SMB는 네트워크 상 존재하는 노드들 간에 자원을 공유할 수 있도록 설계된 프로토콜이다. 주로 네트워크에 연결된 컴퓨터끼리 파일, 프린터, 포트 또는 기타 메시지를 전달하는데 사용된다. SMB 대신 Common Internet File System, CIFS라고도 한다.

SMB는 1990년에 도스, 윈도우, NT, OS/2, 유닉스 등 난잡했던 운영체제 간 자원 공유를 쉽게 해줄 목적으로 만들어졌고, 윈도우로 거의 통일된 지금도 NAS, 네트워크 스캐너 등 리눅스 기기와의 파일공유를 클라이언트 없이 탐색기로 직접 수정가능하다는 장점이 있어 아직도 널리 쓰이고 있다. 리눅스에서는 SAMBA라는 소프트웨어를 설치하면 누구나 쉽게 SMB를 이용한 공유가 가능하다.

이 프로토콜은 윈도우에 종속되어 있어 호환성때문에 수십년간 낡은 코드를 유지하면서 자리를 지켜왔으나, 엄청나게 많은 보안 취약점과 도저히 수정할 수 없는 코드로 인해 마이크로소프트의 골칫거리가 되어왔다. 특히 2017년 5월 발생한 WannaCry라는 랜섬웨어가 바로 SMB의 취약점을 노린 것으로 드러났다.

애플 진영에서는 이것 대신 AFP라는 유사한 프로토콜을 주로 사용했으나 MacOS Big Sur부터 SMB로 대체되었고 iOS도 13부터 네이티브로 SMB를 지원하기 시작했다.


 

감사합니다/

https://arxiv.org/abs/2312.07935

 

Comparing YOLOv8 and Mask RCNN for object segmentation in complex orchard environments

Instance segmentation, an important image processing operation for automation in agriculture, is used to precisely delineate individual objects of interest within images, which provides foundational information for various automated or robotic tasks such a

arxiv.org

 

Object Detection 모델을 생성해야해서 어떤 모델을 사용해야할까 고민하던 와중에 좋은 논문이 있어서 소개드립니다.

Mask R-CNN과 Yolo 모델, 둘 다 Image data 기반의 학습(Object detection)에서 좋은 성능을 보여줍니다만, 요즘의 트렌드를 파악하기 위해 자료를 찾던 중 훌륭한 논문이 있었습니다.

 

결론적으로...


이 연구는 농업 자동화를 위한 YOLOv8과 Mask R-CNN의 객체 분할 성능을 비교합니다. Y

OLOv8은 정밀도와 재현율에서 Mask R-CNN보다 우수하며, 특히 단일 클래스 미성숙 녹색 과일 분할(정밀도 0.92, 재현율 0.97)과 다중 클래스 나무 줄기 및 가지 감지에서 높은 성능을 보였습니다. 또한 YOLOv8은 더 빠른 추론 속도(단일 클래스 128.21 FPS, 다중 클래스 91.74 FPS)를 제공해 실시간 농업 작업에 적합합니다. Mask R-CNN은 속도 면에서 다소 뒤쳐집니다.

 

2024년 상반기 시점에서 YOLOv8이 전반적으로 우수하다는 걸 알 수 있었습니다.

 

에러화면

 

 

AI 학습데이터에 활용할 데이터셋을 불러오는 중에 아래와 같은 에러가 발생했습니다.

 

Error opening file

Unexpected UTF-8 BOM (decode using utf-8-sig): line 1 column 1 (char 0)

Make sure /Users/kaflix_wanya/Desktop/current/카스캐닝 데이터셋/parts_labeling/0831_1996개_jpg+json/100210979526_576120.json is a valid label file.'

 

문제는 annotating, labeling 데이터를 저장하고 있는 json 파일을 불러오면서 발생한 에러인데요.

이 많은 json파일의 인코딩을 어떻게 바꿀까 고민하다가 파이썬 코드로 해결하였습니다.

 

데이터셋 경로를 불러와서 -> 데이터셋 경로 내의 모든 json 파일을 읽어온 후 -> UTF-8로 다시 인코딩 -> 변경된 인코딩형식으로 json파일 저장

 

위 순서대로 작업한 후 다시 labelme에서 'open dir'을 통해 학습데이터 경로를 불러오니 오류없이 잘 작동합니다.

아래는 코드입니다.

import os
import json

def convert_json_encoding(folder_path):
    # 주어진 폴더 내의 모든 파일에 대해 반복
    for filename in os.listdir(folder_path):
        filepath = os.path.join(folder_path, filename)
        
        # 파일이 JSON 파일인지 확인
        if filename.endswith('.json'):
            # JSON 파일을 UTF-8 인코딩으로 읽어들임
            with open(filepath, 'r', encoding='utf-8-sig') as file:
                data = json.load(file)
            
            # 같은 파일을 UTF-8 인코딩으로 다시 저장
            with open(filepath, 'w', encoding='utf-8') as file:
                json.dump(data, file, ensure_ascii=False, indent=4)

# 변경할 폴더 경로를 지정
folder_path = '/User/Dataset'

# 함수 호출
convert_json_encoding(folder_path)

python으로 postgres를 다룰 수 있습니다.

 

 

1. 파이썬 패키지 설치

postgres를 다루기 위한 파이썬 패키지(psycopg2)을 설치해줍니다.

pip install psycopg2-binary

 

2. postgres DB 연결

# PostgreSQL 연결 정보 설정
db_params = {
    "dbname": "postgres",
    "user": "postgres",
    "password": "1234",
    "host": "localhost",
    "port": "5432"
}

# PostgreSQL 연결
conn = psycopg2.connect(**db_params)

위 코드로 간단하게 연결할 수 있습니다.

db접근을 위한 연결정보인 db_params 변수에 오타나 잘못된 값이 입력되면 psycopg2.connect() 함수가 실행될 때 에러가 발생합니다.

오류가 발생했다면 DB가 제대로 설치되었는지, 오타가 없는지 확인이 필요합니다.

 

3. 간단한 테이블을 생성하고 INSERT 쿼리를 실행해보기

# 테이블 생성 쿼리
create_table_query = """
CREATE TABLE IF NOT EXISTS logging (
    log_id SERIAL PRIMARY KEY,
    log_datetime TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    car_number VARCHAR(20),
    image_file_name VARCHAR(255),
    json_file_name VARCHAR(255),
    saved_location VARCHAR(255)
);
"""

cursor = conn.cursor()
cursor.execute(create_table_query)



# 로그 정보
log_data = {
    "car_number": "ABC123",
    "image_file_name": "example_image.jpg",
    "json_file_name": "example_json.json",
    "saved_location": "/path/to/save/location"
}

# 로그 정보 삽입
insert_query = """
INSERT INTO logging (car_number, image_file_name, json_file_name, saved_location)
VALUES (%s, %s, %s, %s);
"""
cursor.execute(insert_query, (
    log_data["car_number"],
    log_data["image_file_name"],
    log_data["json_file_name"],
    log_data["saved_location"]
))

# 변경사항 커밋
conn.commit()

# 연결 및 커서 닫기
cursor.close()
conn.close()

 

위 예제는 주차장이나 건물 차량 입구의 입출차에 대한 cctv 기록을 저장하는 테이블입니다.

'logging'이라는 테이블을 만들고  자동차 번호, 차량 촬영 이미지, 차량 정보 json 파일, cctv 위치 등의 데이터를 insert 해봤습니다.

 

 

DBeaver로 도 확인해보니 데이터가 잘 삽입되었네요 :)

oracle, bigquery만 주로 활용하다가 postgres를 사용할 일이 생겼습니다.

매우 반갑죠.

설치부터 시작하고 postgres에 대해 알아가야겠습니다.


설치

macOS환경에서 Docker로 설치합니다.

(Docker가 설치되어있다는 가정하에) 매우 간단해요.

docker pull postgress
docker run --name my-postgres-container -e POSTGRES_PASSWORD=1234 -p 5432:5432 -d postgres
docker exec -it my-postgres-container psql -U postgres

 

* POSTGRES_PASSWORD와 포트는 사용환경에 맞게 설정하시면 됩니다.

저는 테스트용이기에 1234로 작성했습니다

 

DB 접속

DBeaver를 실행하여 설치한 postgress DB에 접속이 잘 되는지 확인해보겠습니다.

 

 

 

접속이 잘 됩니다.

 

Bigquery Table → CSV file download-> Python Dataframe Read 과정에서 발생한 문제

파이썬으로 데이터 분석을 위해 빅쿼리 테이블을 csv로 받았습니다. (google.bigquery packages는 사용하지 않음)

연, 월, 일만을 갖고있는 Datetime을 나타내는 컬럼의 포맷을 변경하면서 발생한 문제입니다.


1. bigquery 테이블을 가져온 csv 파일 Read

Datetime 컬럼 예시

위 컬럼을 yyyy-mm-dd 형태로 포맷을 변경할거죠

 

2. dt.strtime() 메소드 사용 → 에러 발생

dataframe_of_csv = pd.read_csv('bigquery_table.csv')
dataframe_of_csv["SEARCH_DATE"] =  dataframe_of_csv['SEARCH_DATE'].dt.strftime('%Y-%m-%d')

"AttributeError: Can only use .dt accessor with datetimelike values" 에러가 발생합니다

Can only use .dt accessor with datetimelike values는 .dt 접근자는 datetimelike 값에만 사용할 수 있다는 의미입니다. 즉, 해당 컬럼이 datetimelike 데이터 형식이 아니라서 발생한 오류입니다.

 

3. pd.to_datetime(dataframe['컬럼명']) 메소드 사용 → 해결

코드를 아래와 같이 수정하여 원하는 형태로 formmating을 할 수 있습니다.

dataframe_of_csv = pd.read_csv('bigquery_table.csv')
dataframe_of_csv['SEARCH_DATE'] = pd.to_datetime(dataframe_of_csv['SEARCH_DATE'])

Datetime 컬럼 변환 예시

 


결론

bigqeury, csv(file), python pandas(dataframe) 등 테이블 형태를 여러 방법으로 다루다보면 column의 데이터 형으로 인한 문제는 흔하게 발생합니다.

특히 Datetime의 경우 ETL과정에서 형식의 일치가 무엇보다 중요하죠

외부서비스에서 제공하는 데이터를 관리용 웹사이트를 이용중인 상황.

하지만 일괄적으로 입력할 데이터 양도 많고 input tag에 값을 일일이 넣을 경우 사이트가 느려지거나 여러 값들을 일일이 넣어줘야하는 번거로운 상황이 있었다.


1. Python requests와 BeautifulSoup4 package 이용

 데이터를 입력하고 submit할 때 호출하는 api와 payload(parameters)값들을 추출하였고 웹브라우저 상에서 테스트 시 정상적으로 작동하였지만 requests 패키지를 활용하여 api를 다루다보니 '세션'관련 문제가 발생하였다.

 form-data에 id와 pw를 입력하여 post로 전송하였으나 이 역시 마찬가지 였으며, headers의 세팅 또한 웹브라우저 환경과 동일하게 하였으나 마찬가지 였다.


2. python selenium 활용

입력할 값들을 미리 변수에 넣어서 selenium으로 자동화를 하려하였으나, 관리하는 데이터의 값들과 사이트 폼이 달라서 사실상 의미없는 작업이었다.


3. 세션유지를 위한 문제해결 방법!

결론적으로 requests를 활용하여 api를 호출하는 방법이 가장 효과적이었기에, 세션 유지를 하고자 1 + 2 방법을 혼용하였다.

최초엔 selenium으로 해당사이트에 로그인을 한 뒤, session 값을 가져와서 requests로 제어하니 원하던 작업을 진행할 수 있었다.

 

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
import time

driver = webdriver.Chrome(service= Service(ChromeDriverManager().install()))

url = "https://test.com"
driver.get(url)
time.sleep(5)


import requests
# 여기에서 로그인 작업을 수행
# 사용자명과 비밀번호 입력
username_element = driver.find_element(By.XPATH, '//*[@id="UserId"]')  # 사용자명 입력 필드
password_element = driver.find_element(By.XPATH, '//*[@id="UserPassword"]')  # 비밀번호 입력 필드

username_element.send_keys("id")
password_element.send_keys("password")

# 로그인 버튼 클릭
login_button = driver.find_element(By.XPATH, '//*[@id="myForm"]/button')  # 로그인 버튼 선택
login_button.click()



# Selenium을 사용하여 페이지 이동 및 추가 작업 수행

# 이제 requests를 사용하여 세션을 유지하면서 추가 요청을 보낼 수 있음
session = requests.Session()
# 필요한 추가 요청 설정, 세션에 쿠키 등을 추가
# 예를 들어, session.cookies.update(driver.get_cookies())를 사용하여 브라우저 쿠키를 세션에 추가

# requests를 사용하여 추가 요청을 보내거나 데이터를 추출
response = session.get("https://test.com/example/list")
response.text



# Selenium에서 세션 쿠키 가져오기
selenium_cookies = driver.get_cookies()

# requests 세션 초기화
session = requests.Session()

# Selenium에서 가져온 세션 쿠키를 requests 세션에 추가
for cookie in selenium_cookies:
    session.cookies.set(cookie['name'], cookie['value'])

# requests를 사용하여 추가 요청을 보냄
response = session.get("https://test.com/example/list")




# 요청을 보낼 URL
url = "https://test.com/api/sample"

# POST 요청에 사용할 데이터
data = {
    .........
}

response = session.post(url, data=data)

 

핵심은 마지막 줄 처럼 'requests.post()'가 아닌 'session.post()'로 호출한다는 점! 

url 호출에서 시작되는 일반적인 데이터 수집

실시간으로 변경되는 데이터이며, backfill 처리가 불가능한 수집 형태일 때.

 

1. url 호출 ➡️ 응답값(보통 json) ➡️ 전처리/가공 ➡️ Storage (DB, file ...)

 하나의 실행파일 안에서 자주 구축하던 형태였으나, 예외가 발생할 수 있는 좋지 않은 방식이다.

바로 '전처리' 단계에서 에러가 빈번하게 발생하게 된다.

 - (url의 host나 parameter들의 변경이 없다는 가정하에) respose 값이나 형태가 변경되는 경우

 - 전처리, 가공 후 storage에 저장하는 과정에서 발생하는 type 관련된 이슈

  위와 같은 상황은 꽤 흔하게 발생한다.

 문제는 바로 에러가 생길 때마다 강제로 형 변환(casting)을 해주거나 전처리나 가공하는 과정에서 변경된 'key:value'만 업데이트 해주었을 뿐 'raw data' 수준에서 생각하지 못하였던 것이었다.

 코드를 수정하는 과정에서 시간이 지체되면 수집 주기가 짧은 데이터의 경우 데이터를 수집하지 못하는 공백이 발생하게 되어 결국 데이터 손실이라는 치명적인 결과를 야기할 수 있다.

 

2. raw data를 저장하고 읽어와서 처리하는 파이프라인을 나눈다

1단계 : [url 호출 ➡️ 응답값(보통 json) ➡️ raw 데이터  ➡️ Storage (DB, file ...)] 
2단계 : [query or read file ➡️ 전처리/가공]

파이프 라인이 늘어났다고 볼 수 있으나 안정성은 훨씬 좋아진 결과를 낳았다.

 응답값을 저장하기 전에 별도로 처리하지 않고 바로 저장하게 됨으로써 안정성과 더불어 '데이터 공백'이라는 결정적인 문제가 해결되었다.

기존의 전처리/가공 후에 데이터를 저장하는 과정에서 가장 흔하게 발생하였던 데이터 형 변환 관련된 이슈를 해결하기 위한 시간을 여유롭게 확보할 수가 있으며, 이러한 시간동안에 채우지 못한 데이터 backfill이 가능한 환경을 만든 것이라 할 수 있다.

 

3. 왜 '전처리/가공 ➡️ Storage (DB, file ...)'에서 형 변환 관련된 이슈가 빈번하다고 하는가?

 Storage라 묶었지만, DB(Oracle, Mssql, mysql 등)과 NAS나 서버의 저장공간, 클라우드 서버의 storage 등에 데이터를 파일 형태로 저장하면서 데이터 형식에 관한 이슈가 자주 발생하게 된다.

가장 대표적인 것은 'str, object, datetime 🔁 int(int64, float....)'로 변환하는 과정이다.

 dataframe으로 가공한 데이터를 DB Table에 insert, update 할 때 'Attribute'의 type에 맞지 않는 문제, bigquery도 마찬가지이며 dataframe을 parquet로 변환하는 과정에서도 자주 발생한다. 이럴 때에는 parquet보다 csv로 저장하면 '일다 저장'하는데 문제가 발생하지 않는다.

 저장하려는 공간마다 형태가 제각각이기 때문이다.

 

URL을 호출하여 수집하는 데이터 파이프라인 구축에 대한 결론은

  • 먼저 Raw data를 '일단 수집'한다
  • 가공/데이터 처리 단계를 수집단계와 별도로 분리한다
  • Raw data는 가급적 txt나 json 형태 그대로 파일로 바로 저장하는 것이 좋으나, 수집주기가 짧고 많은 양의 데이터라면 Raw data를 바로 parquet, csv 등의 파일 형태로 변환하여 저장해준다.
  • 양에 따라 데이터가 많으면 parquet, 적을 경우 json, csv 파일로 저장하는 것이 좋다.

json이나 txt 형태의 raw data를 바로 parquet로 변환할 때 가끔 에러가 발생하게 된다.

parquet 압축방식에서 발생하는 문제로 이 이슈는 다음에 자세히 다뤄볼 것이다.

 

데이터 엔지니어링 업무를 맡은지 1년이 훌쩍 지났지만 데이터 수집 과정의 파이프라인을 구축할 때마다 너무 '개발적인'부분에 시간을 할애한게 아닌가 라는 생각이 번뜩 들게 되었다.

 

+ Recent posts