OCI Compartment

 

OCI(Oracle Cloud Infrastructure)의 Compartment는 클라우드 리소스를 효율적으로 구성하고 관리하기 위한 핵심적인 기능입니다. 마치 폴더처럼 리소스를 논리적으로 그룹화하여 격리하고, IAM 정책을 통해 접근 권한을 세밀하게 제어할 수 있습니다.

Compartment를 사용하면 개발, 테스트, 운영 환경을 분리하여 보안을 강화하고, 프로젝트별 또는 부서별로 리소스를 관리하여 비용을 추적하고 예산을 효과적으로 운영할 수 있습니다. 계층 구조를 지원하여 조직 구조를 반영한 리소스 관리가 가능하며, 정책을 적용하여 특정 규칙을 준수하도록 강제할 수 있습니다.

OCI의 Compartment는 단순히 리소스를 묶는 것을 넘어, 클라우드 환경의 보안, 비용, 관리 효율성을 극대화하는 데 필수적인 요소입니다. 체계적인 Compartment 설계를 통해 클라우드 리소스를 안전하고 효율적으로 운영하고 관리할 수 있습니다.

 

OCI IAM

OCI IAM(Identity and Access Management)은 Oracle Cloud Infrastructure에서 누가 어떤 리소스에 액세스할 수 있는지 제어하는 서비스입니다. 간단히 말해, 클라우드 리소스에 대한 액세스 권한을 관리하는 '보안 관리자'라고 생각하시면 됩니다.

IAM 권한이란?

IAM 권한은 사용자, 그룹, 서비스 주체가 특정 리소스에 대해 수행할 수 있는 작업을 정의합니다. 예를 들어, 특정 사용자가 스토리지 버킷을 읽을 수 있지만 쓸 수는 없도록 설정하거나, 특정 그룹이 컴퓨트 인스턴스를 생성하고 삭제할 수 있도록 설정할 수 있습니다.

 

OCI Compartment 생성

Step 1.OCI 접속 후 홈 화면

 

 

 

Step 2. 우측상단 탐색메뉴(햄버거메뉴) 클릭

Step 3. ID & 보안 > 구획 클릭

 

Step 4. 컴파트먼트 생성 클릭

Step 5. 컴파트먼트 생성
이름, 설명을 입력하고 상위 컴파트먼트를 선택합니다.
상위 컴파트먼트는 초기 설정 시 테넌시Name으로 지정하시면 됩니다.
설정이 끝나면 컴파트먼트 생성을 클릭합니다. 

 

Step 6. 컴파트먼트 생성 확인
Test01이라는 컴파트먼트가 생성된 것을 확인하실 수 있습니다.

들어가며

Dataware 표준화 관리 업무를 진행하면서 사용자가 신청한 단어와 유사한 단어가 Dataware 사전에 등록되어 있는지 찾는 일이 간혹 발생했습니다.
예를들어 신청자가 "척도"라는 단어를 등록요청을 하면, 저는 척도라는 단어와 유사한 것들이 있는지 확인이 필요했습니다.
매번 네이버 사전과 Dataware를 넘나들며 단어를 찾느라 소모되는 시간이 너무 아쉽게 느껴져서 LLM의 도움을 받아서 간단한 코딩을 해보자라고 마음을 먹게되어 프로그램을 만들게 되었습니다.

작동방식

1. Python에서 유의어 검색할 단어를 입력
2. 입력 받은 단어를 Ollama로 구축한 Local LLM에 유의어 추천 질의
3. LLM이 추천한 유의어들을 Dataware 사전 DB에 쿼리로 질의 (IN Union LIKE) 

접속 환경 ini파일 정보

config.ini 파일 생성

[OracleDB]
# Optional: Set the path to your Oracle Instant Client directory
# Leave empty if PATH is already set correctly in your system environment
client_path = C:\instantclient-basic-windows.x64-23.7.0.25.01\instantclient_23_7
username = 
password = 
dsn = 
table_name = 
column_name = 

[OllamaAPI]
url = 
model = qwen2.5:7b

[AppSettings]
# Default number of synonyms to request from Ollama
default_synonyms = 10

Python 코드

import sys
import requests
import json
import cx_Oracle
import os
import configparser
import pprint

from PyQt5.QtWidgets import (QApplication, QWidget, QLabel, QLineEdit,
                             QPushButton, QVBoxLayout, QHBoxLayout,
                             QTextEdit, QListWidget, QMessageBox,
                             QTableWidget, QTableWidgetItem, QHeaderView,
                             QMenu, QAction) # Added QMenu, QAction
from PyQt5.QtCore import Qt, QPoint # Added QPoint

# --- Configuration Loading & Oracle Client Init ---
# ... (Keep this section exactly as before) ...
CONFIG_FILE = 'config.ini'
config = configparser.ConfigParser()

if not os.path.exists(CONFIG_FILE):
    print(f"오류: 설정 파일 '{CONFIG_FILE}'을(를) 찾을 수 없습니다.")
    print("스크립트와 동일한 디렉토리에 config.ini 파일을 생성해주세요.")
    exit()

try:
    config.read(CONFIG_FILE, encoding='utf-8')

    oracle_client_path = config.get('OracleDB', 'client_path', fallback=None)

    if oracle_client_path:
        if sys.platform == "win32":
            separator = ";"
        else:
            separator = ":"

        current_path = os.environ.get('PATH', '')
        if oracle_client_path not in current_path:
             os.environ['PATH'] = oracle_client_path + separator + current_path
             print(f"Oracle Client 경로 추가: {oracle_client_path}")
        # else:
        #      print(f"Oracle Client 경로가 이미 PATH에 존재: {oracle_client_path}") # Less verbose

    try:
        # On modern cx_Oracle with PATH set, this might not strictly be needed
        # but doesn't hurt if called correctly.
        # Pass lib_dir only if required by your specific setup/version.
        # cx_Oracle.init_oracle_client(lib_dir=oracle_client_path if oracle_client_path else None)
        cx_Oracle.init_oracle_client()
        print("Oracle Client 라이브러리 초기화 시도 완료")
    except Exception as e:
        # Make this non-fatal unless connection definitely fails later
        print(f"경고: Oracle Client 라이브러리 초기화 중 오류 발생 (연결 시 문제될 수 있음): {e}")
        # print("config.ini의 client_path 설정 또는 시스템 PATH 환경 변수를 확인하세요.")
        # exit() # Don't exit here, let connect fail if needed

except configparser.Error as e:
    print(f"설정 파일 '{CONFIG_FILE}' 읽기 오류: {e}")
    exit()
except KeyError as e:
    print(f"설정 파일 '{CONFIG_FILE}'에 필요한 키가 없습니다: {e}")
    exit()

# --- Functions (get_synonyms, check_synonyms_in_db) ---
# ... (Keep these functions exactly as before) ...
def get_synonyms(word, config, num_synonyms=10):
    """requests를 사용하여 Ollama API를 호출하고 유의어 목록을 가져옵니다."""
    try:
        url = config.get('OllamaAPI', 'url')
        model = config.get('OllamaAPI', 'model')
    except (configparser.NoSectionError, configparser.NoOptionError) as e:
         print(f"설정 파일 오류 (OllamaAPI 섹션): {e}")
         return []

    headers = {"Content-Type": "application/json"}
    data = {
        "model": model,
        "prompt": f"'{word}'의 유의어 {num_synonyms}개를 추천해줘. 각각 쉼표로 구분해서 나열해줘. 반드시 한글사전에 있는 한글 단어들만 추천해줘.\
         한문으로 된 것들은 유의어에서 제거해줘",
        "stream": False
    }
    try:
        # Increased timeout slightly
        response = requests.post(url, headers=headers, data=json.dumps(data), timeout=45)
        response.raise_for_status()

        response_json = response.json()
        synonym_string = response_json.get('response', '').strip()
        # Fallback for potential other key names if needed
        if not synonym_string:
             synonym_string = response_json.get('generation', {}).get('completion', '').strip()

        if not synonym_string:
             print(f"Ollama API 응답에서 유의어 문자열을 찾을 수 없음. 응답: {response.text}")
             return []

        synonyms = [s.strip().replace("'", "") for s in synonym_string.split(",") if s.strip()]
        return synonyms
    except requests.exceptions.Timeout:
        print(f"Ollama API 호출 시간 초과 ({url})")
        return []
    except requests.exceptions.RequestException as e:
        print(f"Ollama API 호출 중 오류 발생 ({url}): {e}")
        return []
    except json.JSONDecodeError as e:
        print(f"JSON 디코딩 오류: {e}. 응답: {response.text}")
        return []
    except KeyError:
        # Less likely with .get() used above, but kept for safety
        print(f"Ollama API 응답 형식 오류. 응답: {response.text}")
        return []

def check_synonyms_in_db(synonyms, word, config, log_text_widget):
    """
    유의어, 입력 단어, LIKE 검색 결과를 Oracle DB 테이블에 존재하는지 확인하고,
    실행된 SQL 쿼리 로그를 반환합니다.
    """
    connection = None
    cursor = None
    sql_logs = []
    existing_synonyms = []

    try:
        username = config.get('OracleDB', 'username')
        password = config.get('OracleDB', 'password')
        dsn = config.get('OracleDB', 'dsn')
        table_name = config.get('OracleDB', 'table_name')
        column_name = config.get('OracleDB', 'column_name')
    except (configparser.NoSectionError, configparser.NoOptionError) as e:
        log_text_widget.append(f"[오류] 설정 파일 오류 (OracleDB 섹션): {e}")
        return [], sql_logs

    try:
        log_text_widget.append(f"DB 연결 시도: {username}@{dsn}")
        connection = cx_Oracle.connect(username, password, dsn)
        cursor = connection.cursor()
        log_text_widget.append("DB 연결 성공.")

        search_terms = synonyms + [word] if word else synonyms # Avoid adding empty word if input was empty

        # 1. IN 조건 검색
        if search_terms:
            sql_in = ""
            bind_variables = {}
            try:
                placeholders = ", ".join(":" + str(i) for i in range(len(search_terms)))
                # Using f-string safely as column/table names come from config
                sql_in = f'SELECT "{column_name}" FROM {table_name} WHERE "{column_name}" IN ({placeholders})'
                bind_variables = {str(i): term for i, term in enumerate(search_terms)}

                sql_logs.append({"type": "IN", "sql": sql_in, "params": bind_variables})
                cursor.execute(sql_in, bind_variables)
                results_in = [row[0] for row in cursor.fetchall() if row[0] is not None]
                existing_synonyms.extend(results_in)
                log_text_widget.append(f"DB 결과 (IN): {results_in}")
            except cx_Oracle.DatabaseError as db_err:
                 log_text_widget.append(f"[오류] DB IN 검색 오류: {db_err}")
                 if sql_in and (not sql_logs or sql_logs[-1]['sql'] != sql_in): # Log if not already logged
                     sql_logs.append({"type": "IN (Error)", "sql": sql_in, "params": bind_variables})

        # 2. LIKE 조건 검색 (Only if word is not empty)
        if word:
            sql_like = ""
            like_pattern = f"%{word}%"
            try:
                sql_like = f'SELECT "{column_name}" FROM {table_name} WHERE "{column_name}" LIKE :word_pattern'
                sql_logs.append({"type": "LIKE", "sql": sql_like, "params": {"word_pattern": like_pattern}})
                cursor.execute(sql_like, word_pattern=like_pattern)
                results_like = [row[0] for row in cursor.fetchall() if row[0] is not None]
                existing_synonyms.extend(results_like)
                log_text_widget.append(f"DB 결과 (LIKE): {results_like}")
            except cx_Oracle.DatabaseError as db_err:
                log_text_widget.append(f"[오류] DB LIKE 검색 오류: {db_err}")
                if sql_like and (not sql_logs or sql_logs[-1]['sql'] != sql_like):
                     sql_logs.append({"type": "LIKE (Error)", "sql": sql_like, "params": {"word_pattern": like_pattern}})

        # 중복 제거 및 정렬
        existing_synonyms = sorted(list(set(existing_synonyms)))
        log_text_widget.append(f"DB 최종 결과 (중복 제거): {existing_synonyms}")
        return existing_synonyms, sql_logs

    except cx_Oracle.Error as error:
        log_text_widget.append(f"[오류] Oracle DB 연결 또는 작업 오류: {error}")
        # Attempt to return logs gathered so far
        return [], sql_logs
    finally:
        if cursor:
            cursor.close()
        if connection:
            connection.close()
            log_text_widget.append("DB 연결 종료.")


# --- GUI Class ---

class SynonymFinderApp(QWidget):
    def __init__(self, config):
        super().__init__()
        self.config = config
        self.setWindowTitle("유의어 검색기")
        self.setGeometry(100, 100, 900, 700)
        self.initUI()

    def initUI(self):
        # --- Input ---
        self.word_label = QLabel("단어 입력:")
        self.word_input = QLineEdit()
        self.word_input.returnPressed.connect(self.find_synonyms)
        self.find_button = QPushButton("유의어 검색")
        self.find_button.clicked.connect(self.find_synonyms)
        input_layout = QHBoxLayout()
        input_layout.addWidget(self.word_label)
        input_layout.addWidget(self.word_input)
        input_layout.addWidget(self.find_button)

        # --- Results Lists ---
        self.synonyms_label = QLabel("추천 유의어 (Ollama):")
        self.synonyms_list = QListWidget()
        self.synonyms_list.setSelectionMode(QListWidget.ExtendedSelection) # Allow multi-select
        self.synonyms_list.setContextMenuPolicy(Qt.CustomContextMenu)
        self.synonyms_list.customContextMenuRequested.connect(
            lambda pos: self.show_list_context_menu(pos, self.synonyms_list)
        )

        self.db_synonyms_label = QLabel("DB에 존재하는 단어 (검색 결과):")
        self.db_synonyms_list = QListWidget()
        self.db_synonyms_list.setSelectionMode(QListWidget.ExtendedSelection) # Allow multi-select
        self.db_synonyms_list.setContextMenuPolicy(Qt.CustomContextMenu)
        self.db_synonyms_list.customContextMenuRequested.connect(
            lambda pos: self.show_list_context_menu(pos, self.db_synonyms_list)
        )

        synonym_layout = QVBoxLayout()
        synonym_layout.addWidget(self.synonyms_label)
        synonym_layout.addWidget(self.synonyms_list)
        db_synonym_layout = QVBoxLayout()
        db_synonym_layout.addWidget(self.db_synonyms_label)
        db_synonym_layout.addWidget(self.db_synonyms_list)
        results_layout = QHBoxLayout()
        results_layout.addLayout(synonym_layout)
        results_layout.addLayout(db_synonym_layout)

        # --- SQL Log Table ---
        self.sql_log_label = QLabel("실행된 SQL 쿼리:")
        self.sql_log_table = QTableWidget()
        self.sql_log_table.setColumnCount(3)
        self.sql_log_table.setHorizontalHeaderLabels(["Type", "SQL Query", "Parameters"])
        self.sql_log_table.setEditTriggers(QTableWidget.NoEditTriggers)
        # Allow selection of cells/rows
        self.sql_log_table.setSelectionBehavior(QTableWidget.SelectItems) # Select individual cells
        # self.sql_log_table.setSelectionBehavior(QTableWidget.SelectRows) # Or select whole rows
        self.sql_log_table.setSelectionMode(QTableWidget.ExtendedSelection) # Allow multi-select
        header = self.sql_log_table.horizontalHeader()
        header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QHeaderView.Stretch)
        header.setSectionResizeMode(2, QHeaderView.ResizeToContents) # Resize params initially
        header.setStretchLastSection(False) # Prevent last section auto-stretch if params is narrow
        self.sql_log_table.verticalHeader().setVisible(False)
        self.sql_log_table.setMaximumHeight(180) # Slightly taller
        self.sql_log_table.setContextMenuPolicy(Qt.CustomContextMenu)
        self.sql_log_table.customContextMenuRequested.connect(self.show_table_context_menu)


        # --- Main Log ---
        self.log_label = QLabel("상세 로그:")
        self.log_text = QTextEdit()
        self.log_text.setReadOnly(True)
        # QTextEdit already supports Ctrl+C and right-click copy by default

        # --- Main Layout ---
        main_layout = QVBoxLayout()
        main_layout.addLayout(input_layout)
        main_layout.addLayout(results_layout)
        main_layout.addWidget(self.sql_log_label)
        main_layout.addWidget(self.sql_log_table)
        main_layout.addWidget(self.log_label)
        main_layout.addWidget(self.log_text)
        self.setLayout(main_layout)

    # --- Context Menu Methods ---

    def show_list_context_menu(self, position: QPoint, list_widget: QListWidget):
        """Shows context menu for QListWidget."""
        menu = QMenu()
        copy_action = QAction("복사 (Copy)", self)

        selected_items = list_widget.selectedItems()
        if selected_items:
            copy_action.triggered.connect(lambda: self.copy_list_selection(list_widget))
        else:
            copy_action.setEnabled(False) # Disable if nothing selected

        menu.addAction(copy_action)
        # menu.exec_ -> Display menu at global position
        menu.exec_(list_widget.mapToGlobal(position))

    def copy_list_selection(self, list_widget: QListWidget):
        """Copies selected items' text from QListWidget to clipboard."""
        selected_items = list_widget.selectedItems()
        if selected_items:
            text_to_copy = "\n".join([item.text() for item in selected_items])
            clipboard = QApplication.clipboard()
            clipboard.setText(text_to_copy)
            self.log_text.append(f"[정보] {len(selected_items)}개 항목이 클립보드에 복사되었습니다.")

    def show_table_context_menu(self, position: QPoint):
        """Shows context menu for QTableWidget."""
        menu = QMenu()
        copy_action = QAction("선택 셀 복사 (Copy Selected)", self)

        selected_items = self.sql_log_table.selectedItems()
        if selected_items:
            # We can enhance this later to copy in TSV format if needed
            copy_action.triggered.connect(self.copy_table_selection_simple)
            # copy_action.triggered.connect(self.copy_table_selection_tsv) # Alternative
        else:
            copy_action.setEnabled(False)

        menu.addAction(copy_action)
        menu.exec_(self.sql_log_table.mapToGlobal(position))

    def copy_table_selection_simple(self):
        """Copies text of selected table cells (newline separated) to clipboard."""
        selected_items = self.sql_log_table.selectedItems()
        if selected_items:
            # Sort items by row, then column for predictable order
            selected_items.sort(key=lambda item: (item.row(), item.column()))
            text_to_copy = "\n".join([item.text() for item in selected_items])
            clipboard = QApplication.clipboard()
            clipboard.setText(text_to_copy)
            self.log_text.append(f"[정보] {len(selected_items)}개 테이블 셀 내용이 클립보드에 복사되었습니다.")

    # --- Optional: TSV Copy for Table (more complex) ---
    def copy_table_selection_tsv(self):
        """Copies selected table cells as Tab Separated Values."""
        selected_items = self.sql_log_table.selectedItems()
        if not selected_items:
            return

        # Determine the bounds of the selection
        min_row = min(item.row() for item in selected_items)
        max_row = max(item.row() for item in selected_items)
        min_col = min(item.column() for item in selected_items)
        max_col = max(item.column() for item in selected_items)

        # Create a grid (list of lists) to store the text
        num_rows = max_row - min_row + 1
        num_cols = max_col - min_col + 1
        grid = [["" for _ in range(num_cols)] for _ in range(num_rows)]

        # Populate the grid with selected item text
        for item in selected_items:
            row_index = item.row() - min_row
            col_index = item.column() - min_col
            grid[row_index][col_index] = item.text().replace('\n', ' ') # Replace newlines within a cell

        # Join the grid into a TSV string
        tsv_rows = ["\t".join(col for col in row) for row in grid]
        text_to_copy = "\n".join(tsv_rows)

        clipboard = QApplication.clipboard()
        clipboard.setText(text_to_copy)
        self.log_text.append(f"[정보] 테이블 선택 영역이 TSV 형식으로 클립보드에 복사되었습니다.")


    # --- Other Methods ---

    def add_sql_log_entry(self, query_info):
        """Adds a row to the SQL log table."""
        table = self.sql_log_table
        row_position = table.rowCount()
        table.insertRow(row_position)

        params_str = pprint.pformat(query_info.get("params", {}), indent=1, width=80)

        type_item = QTableWidgetItem(query_info.get("type", "N/A"))
        sql_item = QTableWidgetItem(query_info.get("sql", "N/A"))
        params_item = QTableWidgetItem(params_str)

        table.setItem(row_position, 0, type_item)
        table.setItem(row_position, 1, sql_item)
        table.setItem(row_position, 2, params_item)
        # Auto-adjust row height might be needed if params_str is very long
        # table.resizeRowToContents(row_position)

    def find_synonyms(self):
        word_to_find = self.word_input.text().strip()

        # --- Clear previous results ---
        self.log_text.clear()
        self.synonyms_list.clear()
        self.db_synonyms_list.clear()
        self.sql_log_table.setRowCount(0)
        # ---

        if not word_to_find:
            QMessageBox.warning(self, "입력 오류", "단어를 입력하세요.")
            self.log_text.append("[오류] 단어가 입력되지 않았습니다.")
            # Don't proceed if word is empty
            return # Added return here

        self.log_text.append(f"'{word_to_find}' 검색 시작...")
        QApplication.processEvents()

        try:
            num_synonyms = self.config.getint('AppSettings', 'default_synonyms', fallback=10)
        except (configparser.NoSectionError, configparser.NoOptionError, ValueError):
            num_synonyms = 10
            self.log_text.append("[경고] config.ini의 default_synonyms 설정 오류 또는 없음. 기본값 10 사용")


        # 1. Get synonyms from Ollama
        self.log_text.append(f"Ollama API 호출하여 유의어 검색 ({num_synonyms}개 요청)...")
        QApplication.processEvents()
        synonyms = get_synonyms(word_to_find, self.config, num_synonyms)

        if not synonyms:
            self.log_text.append("[정보] Ollama API에서 추천된 유의어를 찾지 못했거나 API 호출에 실패했습니다.")
        else:
            self.log_text.append(f"Ollama 추천 유의어: {synonyms}")
            self.synonyms_list.addItems(synonyms)
            QApplication.processEvents()

        # 2. Check DB and get SQL logs
        self.log_text.append("Oracle DB에서 단어 확인 중...")
        QApplication.processEvents()
        existing_db_words, executed_queries = check_synonyms_in_db(
            synonyms, word_to_find, self.config, self.log_text
        )

        # Populate SQL Log Table
        for query_info in executed_queries:
            self.add_sql_log_entry(query_info)
        # Adjust column widths after population if needed (especially params)
        self.sql_log_table.resizeColumnToContents(2) # Resize Params column
        # Re-apply stretch to SQL query column if resizing Params shrunk it too much
        self.sql_log_table.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch)


        if existing_db_words:
            self.db_synonyms_list.addItems(existing_db_words)
        # else: # Logging handled within check_synonyms_in_db or based on executed_queries check

        self.log_text.append("검색 완료.")
        QApplication.processEvents()


# --- Main Execution ---

if __name__ == "__main__":
    app = QApplication(sys.argv)
    ex = SynonymFinderApp(config)
    ex.show()
    sys.exit(app.exec_())

 

실행파일로 프로그램화

 

1. Jupyter에서 작성한 Code 로컬로 다운로드

Jupyternotebook 환경에서 파이썬코드를 작성 및 테스트 하여, 로컬로 다운로드 후 실행파일로 변환하였습니다.

 

2. 다운로드 폴더에서 PowerShell 실행

 

3. PowerShell에서 ipynb->py 파일로 컨버트

* PowerShell의 현재 위치가 ipynb파일이 있는 위치인지 확인하고 진행

jupyter nbconvert --to script meta_word_synonym_finder_v1.1.ipynb

 

아래 캡처본과 같이 py파일이 생성된 것을 확인합니다.

 

4.프로그램화

pyinstaller --clean --onedir --windowed meta_word_synonym_finder_v1.1.py
#실행로그
PS D:\meta_word_synonym_finder> pyinstaller --clean --onedir --windowed meta_word_synonym_finder_v1.1.py
748 INFO: PyInstaller: 6.12.0, contrib hooks: 2025.2
750 INFO: Python: 3.12.0
831 INFO: Platform: Windows-10-10.0.19045-SP0
834 INFO: Python environment: C:\Users\user\AppData\Local\Programs\Python\Python312
837 INFO: wrote D:\meta_word_synonym_finder\meta_word_synonym_finder_v1.1.spec
845 INFO: Removing temporary files and cleaning cache in C:\Users\user\AppData\Local\pyinstaller
851 INFO: Module search paths (PYTHONPATH):
['C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python312\\Scripts\\pyinstaller.exe',
 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python312\\python312.zip',
 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python312\\DLLs',
 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python312\\Lib',
 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python312',
 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages',
 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\win32',
 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\win32\\lib',
 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\Pythonwin',
 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\setuptools\\_vendor',
 'D:\\meta_word_synonym_finder']
3123 INFO: checking Analysis
3124 INFO: Building Analysis because Analysis-00.toc is non existent
3126 INFO: Running Analysis Analysis-00.toc
3128 INFO: Target bytecode optimization level: 0
3129 INFO: Initializing module dependency graph...
3133 INFO: Initializing module graph hook caches...
3198 INFO: Analyzing modules for base_library.zip ...
5819 INFO: Processing standard module hook 'hook-encodings.py' from 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\PyInstaller\\hooks'
9627 INFO: Processing standard module hook 'hook-pickle.py' from 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\PyInstaller\\hooks'
10873 INFO: Processing standard module hook 'hook-heapq.py' from 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\PyInstaller\\hooks'
11671 INFO: Caching module dependency graph...
11712 INFO: Looking for Python shared library...
11752 INFO: Using Python shared library: C:\Users\user\AppData\Local\Programs\Python\Python312\python312.dll
11755 INFO: Analyzing D:\meta_word_synonym_finder\meta_word_synonym_finder_v1.1.py
12116 INFO: Processing pre-safe-import-module hook 'hook-typing_extensions.py' from 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\PyInstaller\\hooks\\pre_safe_import_module'
12126 INFO: SetuptoolsInfo: initializing cached setuptools info...
2677 WARNING: Failed to collect submodules for 'setuptools._vendor.packaging.licenses' because importing 'setuptools._vendor.packaging.licenses' raised: ModuleNotFoundError: No module named 'packaging.licenses'
18419 INFO: Processing standard module hook 'hook-charset_normalizer.py' from 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\_pyinstaller_hooks_contrib\\stdhooks'
18594 INFO: Processing standard module hook 'hook-certifi.py' from 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\_pyinstaller_hooks_contrib\\stdhooks'
18784 INFO: Processing standard module hook 'hook-cx_Oracle.py' from 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\_pyinstaller_hooks_contrib\\stdhooks'
18836 INFO: Processing standard module hook 'hook-PyQt5.py' from 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\PyInstaller\\hooks'
19454 INFO: Processing standard module hook 'hook-PyQt5.QtWidgets.py' from 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\PyInstaller\\hooks'
20374 INFO: Processing standard module hook 'hook-PyQt5.QtCore.py' from 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\PyInstaller\\hooks'
20858 INFO: Processing module hooks (post-graph stage)...
20869 WARNING: Hidden import "sip" not found!
20921 INFO: Processing standard module hook 'hook-PyQt5.QtGui.py' from 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\PyInstaller\\hooks'
22795 INFO: Performing binary vs. data reclassification (99 entries)
23127 INFO: Looking for ctypes DLLs
23134 INFO: Analyzing run-time hooks ...
23143 INFO: Including run-time hook 'pyi_rth_inspect.py' from 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks'
23158 INFO: Including run-time hook 'pyi_rth_pyqt5.py' from 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks'
23171 INFO: Processing pre-find-module-path hook 'hook-_pyi_rth_utils.py' from 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\PyInstaller\\hooks\\pre_find_module_path'
23196 INFO: Processing standard module hook 'hook-_pyi_rth_utils.py' from 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\PyInstaller\\hooks'
23214 INFO: Including run-time hook 'pyi_rth_pkgutil.py' from 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks'
23234 INFO: Creating base_library.zip...
23311 INFO: Looking for dynamic libraries
24380 INFO: Extra DLL search directories (AddDllDirectory): ['C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\PyQt5\\Qt5\\bin']
24384 INFO: Extra DLL search directories (PATH): ['C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\PyQt5\\Qt5\\bin']
25645 INFO: Warnings written to D:\meta_word_synonym_finder\build\meta_word_synonym_finder_v1.1\warn-meta_word_synonym_finder_v1.1.txt
25681 INFO: Graph cross-reference written to D:\meta_word_synonym_finder\build\meta_word_synonym_finder_v1.1\xref-meta_word_synonym_finder_v1.1.html
25751 INFO: checking PYZ
25754 INFO: Building PYZ because PYZ-00.toc is non existent
25758 INFO: Building PYZ (ZlibArchive) D:\meta_word_synonym_finder\build\meta_word_synonym_finder_v1.1\PYZ-00.pyz
26158 INFO: Building PYZ (ZlibArchive) D:\meta_word_synonym_finder\build\meta_word_synonym_finder_v1.1\PYZ-00.pyz completed successfully.
26182 INFO: checking PKG
26185 INFO: Building PKG because PKG-00.toc is non existent
26187 INFO: Building PKG (CArchive) meta_word_synonym_finder_v1.1.pkg
26230 INFO: Building PKG (CArchive) meta_word_synonym_finder_v1.1.pkg completed successfully.
26235 INFO: Bootloader C:\Users\user\AppData\Local\Programs\Python\Python312\Lib\site-packages\PyInstaller\bootloader\Windows-64bit-intel\runw.exe
26248 INFO: checking EXE
26250 INFO: Building EXE because EXE-00.toc is non existent
26252 INFO: Building EXE from EXE-00.toc
26265 INFO: Copying bootloader EXE to D:\meta_word_synonym_finder\build\meta_word_synonym_finder_v1.1\meta_word_synonym_finder_v1.1.exe
26413 INFO: Copying icon to EXE
26547 INFO: Copying 0 resources to EXE
26549 INFO: Embedding manifest in EXE
26600 INFO: Appending PKG archive to EXE
26732 INFO: Fixing EXE headers
26835 INFO: Building EXE from EXE-00.toc completed successfully.
26842 INFO: checking COLLECT
26850 INFO: Building COLLECT because COLLECT-00.toc is non existent
26853 INFO: Building COLLECT COLLECT-00.toc
27564 INFO: Building COLLECT COLLECT-00.toc completed successfully.
27577 INFO: Build complete! The results are available in: D:\meta_word_synonym_finder\dist
PS D:\meta_word_synonym_finder>

 

5. 실행확인

컴파일을 진행하게 되면 폴더에 아래와 같은 파일들이 자동으로 생성이 됩니다.

 

실행 결과는 아래와 같습니다.

 

들어가며

AI 기술은 많은 사람들이 관심을 가지고 있습니다. 하지만 상용 서비스중인 LLM, 예를들어 챗GPT 같은 서비스들은 프라이버시, 비용, 오프라인 사용과 같은 문제들이 발생합니다.

개인이나 기업이서 이와 같은 단점을 없애고자 로컬PC나 서버에 LLM을 직접 구축하여 사용하는 방법이 주목받고 있습니다.

그 중 가장 인기있는 오픈소스인 Ollama에 대해서 소개하고자 합니다.

 

Ollama 란?

Ollama는 로컬 컴퓨터에서 대규모 언어 모델(LLM)을 쉽고 빠르게 설치하고 실행할 수 있게 해주는 오픈소스 도구입니다.

쉽게 말해, ChatGPT 같은 AI 모델들을 클라우드 서버가 아닌 사용자 본인의 PC(Windows, macOS, Linux)에서 직접 구동할 수 있도록 도와주는 플랫폼이라고 생각하면 됩니다.

설치 서버 환경

* Linux 8
* GPU 無
* CPU Only 로 구성

 

Ollama 설치방법

1. root로 서버에 접속

su - root

2. Ollama 설치

curl -fsSL https://ollama.com/install.sh | sh

※ 참고 : 제가 구축한 서버 환경에는 GPU 장치가 없어서 CPU-only Mode로 설치된 것을 확인할 수 있습니다.
WARNING: No NVIDIA/AMD GPU detected. Ollama will run in CPU-only mode.

엔비디아 GPU 환경의 서버에서는 아래와 같은 Log가 나타납니다.

Created symlink /etc/systemd/system/default.target.wants/ollama.service /etc/systemd/system/ollama.service.
>>> NVIDIA GPU installed.

3. Ollama 환경설정 확인

vi /etc/systemd/system/ollama.service
[Unit]
Description=Ollama Service
After=network-online.target

[Service]
ExecStart=/usr/local/bin/ollama serve
User=ollama
Group=ollama
Restart=always
RestartSec=3
Environment="PATH=/usr/share/Modules/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin"

[Install]
WantedBy=default.target

위의 내용은 설치 시 기본적으로 설정되어있는 파라미터들 입니다.

 

4. Ollama 모델 저장소 변경을 위한 디렉토리 생성(생략가능)

Ollama에 기본 저장소가 아닌 지정한 저장소 사용을 위해 디렉토리를 생성합니다.

mkdir -p /data/ollama/models
chown -R ollama:ollama /data/ollama

 

5. Ollama 환경설정 변경

저는 추가적으로 Host 정보와 Model 저장소를 지정해였습니다.

#추가 내용

# 외부연결 가능하도록 설정
Environment="OLLAMA_HOST=0.0.0.0:9320"
# 모델 저장소 지정
Environment="OLLAMA_MODELS=/data/ollama/models"

 

6. Ollama 환경설정 적용

# demon 재기동
systemctl daemon-reload
systemctl restart ollama
export OLLAMA_HOST=0.0.0.0:9320

또는

vi .bash_profile
export OLLAMA_HOST=0.0.0.0:9320

 

Ollama에 LLM 모델 설치방법

1.Ollama에 모델 설치

ollama run qwen2.5:7b

2.설치된 모델 테스트

>>> Send a message (/? for help) 에 하고 싶은 질의를 작성 후 보냅니다.

>>> who are you?
>>> who are you?
I am Qwen, a large language model developed by Alibaba Cloud. 
I'm here to assist with a wide range of tasks and provide information on various topics. 
How can I help you today?

3.설치된 모델 테스트 종료

>>> /bye

 

4.설치된 모델 확인

ollama list

 

Ollama에 LLM 모델 삭제

# ollama rm [모델명]
ollama rm qwen2.5:7b

 

벡터인덱스 개념

벡터 임베딩에 대한 벡터 인덱스를 생성하고 인덱스를 사용하여 방대한 벡터 공간에서 유사성 검색을 실행할 있습니다.

벡터 인덱스는 고차원 벡터를 사용하여 유사성 검색을 가속화하도록 설계된 특수 인덱싱 데이터 구조의 종류입니다. 클러스터링, 분할 이웃 그래프와 같은 기술을 사용하여 유사한 항목을 나타내는 벡터를 그룹화하여 검색 공간을 크게 줄여 검색 프로세스를 매우 효율적으로 만듭니다.

 현재 오라클에서 지원하는 벡터 인덱스의 종류는 2가지 입니다.
1.      In-Memory Neighbor Graph = Hierarchical Navigable Small World (HNSW)
2.      Neighbor Partition vector index = Inverted File Flat (IVF) index

 IN-Memory Neighbor Graph Index 계층적 탐색 가능한 소규모 세계(HNSW)유형을 지원하는 인덱스로 벡터 근사 유사성 검색을 위한 매우 효율적인 인덱스입니다.
HNSW 그래프는 계층적(Hierarchical) 구조와 함께 탐색 가능한 소규모 세계(NSW) 네트워크의 원칙을 사용하여 구조화됩니다.

 Neighbor Partition vector index Inverted File Flat(IVF) Index 유일하게 지원하는 벡터 유형의 인덱스입니다. IVF 높은 검색 품질과 적당한 속도의 균형을 이루는 분할 기반 인덱스입니다.
이웃 파티션 또는 클러스터를 사용하여 검색 영역을 좁혀 검색 효율성을 높이기 위해 고안된 기술입니다.

 

HNSW (Hierachical Navigable Small World) 인덱스 이해

HNSW Hierarchical Navigable Small World 약자로 여러 알고리즘들의 결합으로 만들어져 있습니다.
결합된 알고리즘을 분해해서 살펴보면 Small World 특성을 바탕으로 효율적인 경로를 탐색 가능하게 하는  NSW(Navigable Small World)라는 알고리즘에 Skip List 개념을 적용하여 NSW 그래프를 계층화한 HNSW (Hierachical Navigable Small World) 알고리즘이 만들어지게 되었습니다.

 Navigable Small World(NSW) 사용하면 그래프의 벡터가 가지 특성을 기반으로 여러 다른 벡터에 연결되는 근접 그래프를 구축하는 것이 목표입니다.
1.     벡터 사이의 거리
2.     삽입 검색 과정에서 단계에서 고려되는 가장 가까운 벡터 후보들의 최대 (EFCONSTRUCTION)
3.     벡터당 허용되는 최대 연결 (NEIGHBORS)

위의 임계값의 조합이 너무 높으면 밀집 연결 그래프가 되어 검색 프로세스가 느려질 있습니다.

반면, 해당 임계값의 조합이 너무 낮으면 그래프가 너무 희소하거나 연결이 끊어져서 검색 중에 특정 벡터 간의 경로를 찾기 어려울 있습니다.

HNSW 벡터 인덱스는 In-Memory 인덱스이므로 인덱스 세그먼트가 생성되지 않으며, 암시적으로 추가 오브젝트를 생성합니다. *_INDEXES 뷰를 조회하면 2개의 B-tree 인덱스와 3개의 테이블이 생성된 것을 확인할 있습니다.

Small World 6단계의 법칙

출처
§   THE ORACLE OF BACON(https://oracleofbacon.org/)
§   NHN Forward 22 - 벡터 검색 엔진에 ANN HNSW 알고리즘 도입기(https://inspirit941.tistory.com/504)
§   자료구조와 알고리즘 - 벡터 검색 엔진 최적화 방법: HNSW(Hierarchical Navigable Small World Graphs) (https://kokoko12334.tistory.com/m/entry/hsfl)
§   Hierarchical Navigable Small World Graph nearest neighbor 빠르게 찾아 보자. (https://ita9naiwa.github.io/ml/2019/10/04/hnsw.html)
§   검색증강생성(RAG) - 그래프 기반 벡터 인덱스 HNSW(Hierarchical Navigable Small World)
(
https://jerry-ai.com/30
)

스몰월드는 케빈 베이컨 게임으로 설명으로 쉽게 설명이 됩니다.

우리나라의 유명 배우 한명인 송강호 배우가 있습니다. 송강호 배우는 케빈 베이컨과 단계를 거치면 도달할 있을까요? 놀랍게도 3단계만에 송강호 배우와 케빈 베이컨배우가 연결이 됩니다.

케빈 베이컨은 어퓨굿맨 이라는 영화에서 크루즈와 함께 출연을 했습니다. 크루즈는 바닐라 스카이라는 영화에서 틸다 스윈튼배우와 같이 출연을 했습니다. 틸다 스윈튼배우는 설국열차에서 송강호 배우와 같이 출연을 했습니다. 정리를 해보면 아래와 같은 관계가 형성이 됩니다.

케빈 베이컨-어퓨굿맨- 크루즈-바닐라스카이-틸다 스윈튼-설국열차-송강호

그렇다면 케빈 베이컨과 우리나라 배우들과의 관계도 많아야 4~5단계안에 형성이 있습니다.
관계를 일반화해서 전세계에 있는 사람들을 대상으로 생각해 보겠습니다. 전세계에 있는 사람들도 평균적으로 6단계를 거치면 만날 있다라는 네트워크 이론 6단계 법칙 만들어지게 되었습니다.

 

Small World

위의 베이컨 게임을 통해서 보았듯 우리는 수학적으로 6단계안에 관계가 형성되고 있고, 이러한 법칙을 바탕으로 Small World(작은 세계)라는 개념이 탄생하게 되었습니다. 스몰월드라는 네트워크 이론을 조금 더 살펴보겠습니다.

아래의 그래프처럼 모든 네트웍은 연결 방식에 따라 크게 가지로 나눠질 있습니다.
일정한 규칙에 따라 인접한 곳과 일정한 숫자로만 링크되는 'regular network' 있고,
무작위로 서로 연결되어 있는 'random network' 있습니다.
그리고 이 둘의 중간쯤에 있는 Small-world가 있습니다.
스몰월드는 구성원의 일부만이 전혀 엉뚱한 곳으로 연결되어 있는 네트워크입니다.

Small World Network 도식화 그래프에서 스몰월드의 상태를 알아보겠습니다.
가장 왼쪽에 있는 그래프는 모든 Edge(간선,) 고르게(Regular) 연결되어 있는 상태입니다.
가장 오른쪽 그래프는 모든 Edge Random으로 연결된 상태입니다.

왼쪽에 있는 Regular 그래프에서 몇개의 노드를 Random 연결시켜 봤더니 평균적으로 노드에 도달하는 거리의 수가 확연히 줄어드는것을 있습니다.

중간에 있는 Small-world 그래프는 위에 설명처럼 Regular그래프에서 3개의 노드를 랜덤으로 추가하여 연결했더니 전체적인 노드의 길이가 줄어들었습니다.

, 전체 네트워크가 거대하더라도 일부의 특정 노드에 의해서 전체가 서로 가깝게 연결될 있다 것을 의미합니다.

Small world 이론적인 특징을 정리해보면 다음과 같습니다.
1.      짧은 평균 경로 길이 : 베이컨 게임에서 보았듯 Small world 네트워크에서 네트워크 내의 임의의 노드 사이의 평균거리는 매우 짧습니다.
2.      높은 클러스터링(높은 군집계수) : 네트워크는 노드들이 지역적으로 밀집된 클러스터를 형성하는 경향이 있습니다. , 노드의 이웃 노드들이 서로 연결되어 있을 가능성이 큽니다. 친구의 친구도 친구일 가능성이 높다라는 상황으로 나타날 있습니다.
3.      거리 연결 : 비교적 멀리 떨어져있는 노드들이 특별한 연결을 통해 서로 직접 연결되는 것을 의미합니다. 거리 연결(long-range link)" 또는 "지름길(shortcut) 불리는 연결을 통해 전체의 연결성을 크게 개선됩니다.

 

NSW(Navigable Small World)

NSW Small-World Network 특성을 유지하면서 현재 위치에서 가장 가까운 이웃 노드로 이동하여 최단 경로를 찾을 있는 네트워크 구조를 말합니다.

아래의 그림은 벡터값을 표현한 그림입니다.

검정색 선은 Regular 표현된 경로이고, 빨간색 선은 Small World 특징 하나인 Long-range link 표현한 입니다. 시작점인 entry point에서 목표점인 query 벡터를 찾아가보도록 하겠습니다.

검은색 노드들만 따라갔을 4개의 노드를 거쳐서야 가장 가까운 이웃 노드로 이동이 되었는 반면,small world 적용한 빨간색 노드와 섞어서 찾아 갔을때 3개의 노드만 거쳐서 가장 가까운 이웃 노드로 이동이 것을 확인할 있습니다.

하지만 NSW 알고리즘에도 단점이 있습니다.

바로 local minimum 빠질 있다는 문제입니다.

 

NSW(Navigable Small World)의 문제점

NSW의 문제점인 local minimum에 대해 알아보겠습니다.

 Local Minimum : 미니멈은 탐색 과정이 이상 진전되지 않는 지점을 의미.
네트워크 탐색 과정에서 발생할 있는 문제 하나로, 목적지에 도달하기 위해 최단 경로를 찾으려는 탐색이 이상 진행되지 않고 멈추는 지점을 의미합니다. 지점에서는 이상 목적지로 가까워지지 않거나, 오히려 목적지에서 멀어지게 되는 상황이 발생할 있습니다.
실생활을 통해서 예를 들면, 회사의 위치가 서울시 서초구에 위치해 있는데 주소에 대한 링크수가 작다고 하면  회사의 주소는 서울시다 라는 Local minimum 빠질 위험성이 있습니다. 반대로 링크 수를 많이 늘리면 서초구 서초**빌딩 이라는 주소가 나오겠지만 찾아가는 단계는 서울시를 찾는 단계보다 복잡해집니다.

서버의 입장에서 보면 링크수가 늘어나면 정확도는 높아지지만 탐색 시간과 자원은 늘어나게 되는것입니다. 그렇기에 정확도와 자원의 적정선을 찾는 튜닝이 필요합니다.

 

아래에 그래프를 통해 Local Minimum이 발생하는 것을 확인해보겠습니다.

쿼리 벡터 V 네트워크 가장 가까운 노드를 탐색할 , 해당 Entry 진입점에서 시작하여 Greedy 탐색을 통해 가장 가까운 노드를 찾을 있습니다.

하지만 위의 진입점에서 출발하여 Greedy 방식을 적용하면 가장 가까운 노드가 아닌 노드를 선택합니다.
이를 Local minimum 이라고 합니다. 이를 해결하는 방법 중에 하나는 연결하는 링크 (m) 늘리는 것입니다. 하지만 이는 정확도가 높아지지만 그만큼 탐색 시간이 늘어나게 됩니다.

문제를 해결하기 위해서 계층 구조(Hierachical) 추가합니다.

계층 구조를 알아보기 전 Greedy알고리즘을 보고 넘어가겠습니다.

 

 

Greedy 알고리즘

Greedy 알고리즘 : 최적의 값을 구해야 하는 상황에서 사용되는 근시안적인 방법론.
아래의 그림으로 Greedy 알고리즘을 사용해서 노드에서 합이 가장 높은 방법 찾는 문제를 해결해보겠습니다.

1.      처음에는 루트노드에서 시작하므로 5 정점을 선택이 됩니다.
2.      두번째는 브런치노드에서 3개의 정점 7,10,8 대해 비교 현재 노드에서 가장 10 선택합니다.
3.      마지막으로 리프노드에서 2개의 정점 4,3 대해 비교 4 선택합니다.

Greedy(탐욕) 알고리즘은 현재 상황에서 가장 적합한 답안을 찾으므로 정답과는 다를 있습니다. 아래의 그림에서는 5->7->9 합인 21 정답이지만 greedy 알고리즘을 사용했을 시에는 노드당 가장 값을 선택하므로 5->10->4 합이 19 답안이 나오게 됩니다.

 

Skip list

앞에서 NSW의 문제점인 Local minimum을 해결하기 위해 계층을 추가하여 HNSW (Hierarchical Navigable Small World)를 만들었습니다.
HNSW(Hierachical Navigable Small World) 근사 최근접 이웃 탐색(Approximate Nearest Neighbor Search) 위해 사용되는 알고리즘으로 NSW 문제점인 로컬미니멈을 해결하기 위해 skip list 개념을 적용하여, NSW 그래프를 계층화한 알고리즘입니다.
계층을 효율적으로 나누기 위해서 Skip list 개념을 사용하는데 잠깐 알아보고 HNSW로 넘어가겠습니다.

기존의 단일 연결 리스트에서 숫자 6 접근하려면, 진입점부터 시작해 노드를 순차적으로 접근해야 합니다.
스킵 리스트는 이러한 단점을 해결하기 위해 다중 레벨 도입하여 빠르게 탐색할 있게 합니다. 스킵 리스트는 여러 레벨의 연결 리스트로 구성되어 있으며, 레벨은 하위 레벨의 노드를 건너뛸 있는 포인터를 포함합니다

Skip list 에는 두가지에 유형이 있습니다.

하나는 완전 스킵리스트이고, 나머지 하나는 랜덤 스킵리스트(=확률 스킵리스트) 입니다.

HNSW에서 사용하는 스킵리스트의 유형은 확률 스킵리스트이므로, 완전 스킵리스트는 간략하게만 살펴보겠습니다.

  • 완전 스킵리스트 : 스킵하는 레벨을 규칙적으로 정해서 구성하는 방법입니다.

기본형태
4레벨

완전 스킵리스트의 방식은 고정된 패턴으로 인해 유연성이 부족하고 특정 상황에서는 비효율적일 있습니다.
검색은 빠르지만, 삽입과 삭제 연산에서 비효율적인 구조를 갖게 됩니다. 삽입과 삭제 하는 연산에서 스킵 리스트를 재구성해야하기 때문입니다.

이를 해결하기 위한 방법이 랜덤 스킵리스트입니다

  • 확률(랜덤) 스킵리스트 : 각 레벨의 노드가 확률적으로 결정되는 방식.

랜덤 스킵리스트는 확률적 접근 방법 통해 삽입 삭제 과정을 더욱 효율적으로 만들어줍니다.
아래에 그래프를 통해 확인해보겠습니다.

 

- 레벨 1에서는 모든 노드가 존재하므로 확률이 1입니다.
- 레벨 2에서 노드를 가질 확률은 1/2입니다.
- 레벨 3에서 노드를 가질 확률은 1/4입니다.
- 레벨 k에서 노드를 가질 확률은 1/(2^(k-1))입니다.

예를 들어, 2 레벨에 노드를 배치할 확률이 1/2, 3 레벨에서는 1/4 식으로 재귀적으로 적용됩니다. 이를 통해 삽입  삭제 과정에서도 평균적인 시간 복잡도를 유지할  있습니다.

이와 같은 확률 할당 방식은 노드가 상위 레벨로 갈수록 점점 적어지도록 만들며, 이를 통해 노드들의 탐색시간을 줄일 있습니다.

참고
- 체크: 오라클에서는 HNSW인덱스가 있는 테이블은 DML문이 안됩니다.
ORA-51928: 인메모리 인접 그래프 벡터 인덱스가 있는 테이블에서는 DML(데이터 조작어) 지원되지 않습니다.
오라클 DB에서는 DML문이 되지 않으므로 확률 스킵리스트의 삽입 삭제의 이점이 작습니다.

하지만, INDEX 생성시에 삽입의 이점을 받을 있고, EFCONSTRUCTION 파라미터를 통해 조정할 있습니다.
인덱스를 스토리지가 아닌 메모리 공간에 생성하여 사용하기 때문에 DML문이 제한 되지않을까라는 생각이 듭니다. 그럼에도 불구하고 확률 스킵리스트를 채택한 이유는 HNSW 알고리즘에서 확률형 스킵리스트가 탐색 효율성 높이는데 효과가 있고, 메모리의 효율성 또한 높일 있기 때문이라고 생각이 듭니다.

- EFCONSTRUCTION:삽입 검색의 단계에서 고려되는 가장 가까운 벡터 후보의 최대 개수

 

HNSW(Hierarchical Navigable Small World)

여러 복잡한 알고리즘들을 알아본 끝에 앞에 알고리즘들의 결합인 HNSW에 대해 알아보겠습니다. 위에서 설명한 바와 같이 HNSW(Hierachical Navigable Small World) 근사 최근접 이웃 탐색(Approximate Nearest Neighbor Search) 위해 사용되는 알고리즘으로 NSW 문제점인 로컬미니멈을 해결하기 위해 skip list 개념을 적용하여, NSW 그래프를 계층화한 알고리즘입니다.

위에서 학습한 Skip list 계층화 하여 NSW 적용을 하면 다음과 같습니다.

시작점은 Entry Pioint인데 가장 높은 계층인 Entry Layer에서 시작을 합니다.

해당 계층에서 가장 가까운 정점을 찾고 다음 계층으로 내려갑니다.

다음계층으로 내려간 해당 계층에서 가장 가까운 정점을 찾고 다음 계층으로 내려간 가장 낮은 계층에서 사용자의 질의 벡터(vq) 가장 근접한(Nearest Neighbor) 벡터를 찾습니다.

이때 계층과 노드가 작으면 Local minimum 빠질 있고, 너무 많으면 시간과 자원의 소모가 커질 위험이 있습니다.

높은 레이어에서는 노드의 수가 작고, 인접한 노드 사이의 거리가 깁니다.
낮은 레이어로 갈수록 노드의 수가 많고, 인접한 노드 사이의 길이가 짧아집니다.

 

실생활에서 예를 들어보겠습니다.

노드 사이의 거리를 효율적으로 계산하기 위해 확률 스킵리스트를 적용해서 노드들을 계층화 합니다.

강릉에서 자갈치 시장을 찾아가는 방법을 생각해봅니다. 시간이라는 자원을 아끼기 위해 가장 상위 계층에서 시작합니다. 서울을 경유해서 가는 거리로만 보면 자갈치 시장으로 바로 가는 직선거리가 가장 짧습니다. 하지만 바로 가기에는 시간이라는 자원이 많이 소모가 됩니다.

그래서 비행기를 타고 한번에 부산까지 이동을합니다. 여기서 비행기는 탈수있는 곳이 적고 한번에 멀리 이동할 있는 특징이 있습니다.
HNSW 그래프에서도 동일합니다. 상위계층의 특징은 노드간의 거리가 멀고 정점의 수가 적습니다.
부산 까지 이동을 자갈치 시장으로 가기위해서 지하철을 타고 이동을 합니다.
자갈치 시장에 가장 가까운 지하철역 내립니다.

과정을 HNSW에서 보면 가장 근접한(Nearest Neighbor) 벡터를 찾는 방법과 동일합니다.

 

마무리

ORACLE Vector Index를 공부하면서 여러 알고리즘들을 접하였는데, 이해가 쉽지않았습니다. 이곳저곳을 찾아가며 정보를 얻은것들을 바탕으로 작성했는데, 오라클에서 추구하는 HNSW와 조금 차이가 있을 수 있습니다.
ORACLE Document에서는 간략하게만 설명이 나와있어서 추가적인 정보를 붙여서 정리했는데 도움이 되었으면 좋겠습니다.

'ORACLE 23ai' 카테고리의 다른 글

ORACLE 23ai RAG(증강검색) 실습  (4) 2024.09.19
Oracle Embedding Model Load  (1) 2024.09.05
Embedding  (1) 2024.08.28
RAG(검색 증강 생성)이란  (0) 2024.08.26

들어가며

앞선 글에서 RAG에 대해 DBA의 관점으로 간략하게 설명을 했습니다.
(RAG(검색 증강 생성)이란 글 참고)
ORACLE DB에서 RAG를 사용하는 방법에 대해 알아보겠습니다.
제가 테스트 한 버전은 23AI Free 버전이므로 추후 EE버전에서는 내용이 달라질 수 있습니다.

실습 시나리오

오라클에서 LOCAL LLM OLLAMA 사용하여 EXAONE MODEL RAG(증강검색) 하는 테스트입니다.
대법원선고중요판결요지 라는 문서에 내용을 Chunk Embbeding 시켜 Vector 값을 가진 테이블을 만들었습니다. 테이블의 데이터를 사용해서 EXAONE LLM Model 질의를 하는 내용으로 테스트 내용을 작성했습니다.

대법원선고중요판결요지파일은 아래의 url 링크를 통해 다운받아서 사용하세요.
파일명 : law240830(8.29.판결).pdf 다운로드 링크

실습

1. 실습 테이블 생성

위에 선고중요판결요지라는 파일을 서버에서 불러와 임베딩하여 Vector값으로 컬럼이 포함된 테이블로 생성합니다.
쿼리 가장 아래에 있는 embed_vector clob 이라는 쿼리 내용중 clob 타입을 vector 했을 시에 테이블에 데이터가 보이지 않는 문제가 발생하여 clob select 절에서 to_vector함수를 사용하여 다시 벡터화 시켰습니다.

create table dre.rag_test as
select embed_id,embed_data,to_vector(embed_vector) as embed_vector
  FROM (select 
dbms_vector_chain.utl_to_text(TO_BLOB(bfilename('DOC_DIR',a.file_name))) AS DOC_content_blob
from (values('대법원선고중요판결요지_240829.pdf')) a (file_name) ) dc 
CROSS JOIN TABLE (
                 dbms_vector_chain.utl_to_embeddings(
                 dbms_vector_chain.utl_to_chunks(
                 dbms_vector_chain.utl_to_text(dc.DOC_CONTENT_BLOB)
                    ,json('{"by":"characters","max":"250","split":"none","normalize":"all","LANGUAGE":"KOREAN"}')),
                        json('{"provider":"database", "model":"ONN.DISTILUSE_BASE_MULTILINGUAL_CASED_V2"}'))) t
CROSS JOIN JSON_TABLE(t.column_value, '$[*]' COLUMNS (embed_id NUMBER PATH '$.embed_id', embed_data VARCHAR2(1000) PATH '$.embed_data', embed_vector clob PATH '$.embed_vector')) AS et;

 

2. HOST_ACL 등록

LOCAL LLM 서버와의 통신을 위해 HOST_ACL 등록합니다.

BEGIN
    dbms_network_acl_admin.append_host_ace(
        host =>'172.17.12.116',
        LOWER_PORT =>'9301',
        UPPER_PORT =>'9301',
        ace => xs$ace_type(
           privilege_list => xs$name_list('http', 'http_proxy'),
           principal_name => 'DRE',
           principal_type => xs_acl.ptype_db)
    );
END;
/

 

3. ACL 등록 통신 Test

SELECT * FROM  DBA_HOST_ACES ACES;
select utl_http.request('http://172.17.12.116:9301') from dual;
#쿼리 결과
Ollama is running

 

4. CREDENTIAL 생성

LLM API key 관리를 위해 CREDENTIAL 오브젝트 생성합니다.

declare
	  v_params json_object_t;
	  v_name varchar2(1000) :=  'EXAONE_CRED';
	  v_api_token varchar2(1000) := 'ollama-key'; 
	begin
	   v_params := json_object_t();
	   v_params.put('access_token',v_api_token); 
	   begin
	        DBMS_VECTOR_CHAIN.DROP_CREDENTIAL ( CREDENTIAL_NAME  => v_name);
	   exception 
	     when others then
	        null;
	   end;
	   
	   DBMS_VECTOR_CHAIN.CREATE_CREDENTIAL ( CREDENTIAL_NAME => v_name, PARAMS => json(v_params.to_string));
	end;
/

 

5. LLM 테스트

ORACLE에서 LLM 서버에 질의를 연결상태를 점검합니다. LLM모델은 exaone3.0:8b 입니다.

declare
  input clob;
  params clob;
  output clob;
begin
  utl_http.set_body_charset('UTF-8'); 
  input := '안녕 exaone3.0:8b 모델에 대해 설명해줘';
  params := '
{
  "provider": "OpenAI",
  "credential_name": "EXAONE_CRED",
  "url": "http://172.17.12.116:9301/v1/chat/completions",
  "model": "exaone3.0:8b",
  "context_length" : 2048
}';
  output := dbms_vector_chain.utl_to_generate_text(input, json(params));
  dbms_output.put_line(output);
  if output is not null then
    dbms_lob.freetemporary(output);
  end if;
exception
  when OTHERS THEN
    
    DBMS_OUTPUT.PUT_LINE (SQLERRM);
    DBMS_OUTPUT.PUT_LINE (SQLCODE);
end;
/
#질의 결과
-------------------------[Start Time: 2024/09/19 15:24:39]-------------------------
SQL> 안녕하세요! LG AI 연구원에서 개발한 EXO(EXpert Operating System) 3.0 기반의 8B 모델, EXAONE 3.0에 대해 설명해드릴게요.

### 주요 특징 및 장점:
1. **고성능 언어 이해 능력**:
   - 한국어와 영어를 포함해 다양한 자연어 처리 작업에서 우수한 성능을 자랑합니다.
  
2. **초거대 AI 모델 기반**: 
   - 8x Nvidia A100 GPU로 학습되었으며, 약 6TFLOPS의 연산 능력을 가집니다. 이는 기존 초거대 인공지능 대비 효율적이고 강력한 계산력입니다.
    
3. **전문 지식 통합**:
   - 방대한 양의 전문 문서 데이터를 바탕으로 다양한 산업의 최신 정보와 전문 지식을 습득할 수 있습니다. 
  
4. **생성적 AI 능력**:
   - 창작 활동 (예: 소설 작성, 음악 작곡 등)에도 뛰어난 성능을 보입니다. 이는 모델이 단순히 데이터를 분석하는데 그치지 않고 새로운 지식과 콘텐츠를 생성해 낼 수 있음을 의미합니다.
    
5. **한국어 특화 학습 모델**: 
   - 한국어에 최적화된 언어 모델로 감정 분석, 번역 작업 등에서도 우수한 성과를 보이며, 멀티모달 데이터 처리 기능이 강화되어 이미지 인식까지 확장할 수 있습니다.
  
6. **데이터 학습 윤리 및 프라이버시 준수**: 
    - 개인정보 보호 규정과 윤리적인 AI 개발 가이드라인을 철저히 준수하여 안전하게 활용 가능합니다.
   
### 결론적으로,
EXAONE 3.0은 여러 산업의 전문 작업 지원, 창의성 발휘 등 다양한 방면에서 효과적으로 사용될 수 있는 차세대 인공지능 모델입니다. 여러분께서 필요로 하시는 특정 분야나 적용 사례가 있다면 더 구체적인 설명도 가능하니 말씀해 주세요!

 

6. RAG사용을 위한 함수 생성

LLM모델은 위의 테스트에서 사용한 exaone3.0:8b 이고, Oracle DB안에 로드한 Embbeding Model distiluse_base_multilingual_cased_v2 사용하여 임베딩했습니다.

펑션의 내용을 보면 p_user_question 이라는 변수를 사용자에게 입력 받아옵니다.
입력받은 데이터를 v_model_params JSON 타입으로 작성된 모델환경으로 질의를 날립니다.
사용자 질의 내용은 오라클 안의 임베딩 모델을 통해 사용자 질의 쿼리 벡터를 생성하고, 쿼리 벡터를 사용하여 DRE.RAG_TEST라는 테이블에 VECTOR_DISTANCE 함수로 벡터거리를 계산합니다.
v_k_stop_count 파라미터의 값만큼 찾고 조회를 STOP합니다. 나온 결과들을 v_sys_instruction 정의된 내용과 v_message,v_user_question 등을 합하여 v_prompt 만들어 dbms_vector_chain.utl_to_generate_text 통해 LLM 질의를 합니다.

CREATE OR REPLACE FUNCTION DRE.fn_rag_test(p_user_question VARCHAR2) RETURN CLOB  
AUTHID DEFINER
IS
  v_model_params JSON ;
  v_user_question_vec vector; 
  v_prompt varchar2(1000);  
  v_sys_instruction varchar2(1000);
  v_message clob; 
  v_user_question varchar2(1000); 
  v_k_stop_count number := 1;
  output clob;
  
BEGIN
  v_model_params := JSON('
    {
    "provider":"OpenAI",
    "credential_name":"EXAONE_CRED",
    "url": "http://172.17.12.116:9301/v1/chat/completions",
    "model": "exaone3.0:8b",
    "context_length" : 2048
    }') ;
  -- JSON형식에서 질의내용만 추출 :
  v_sys_instruction := 'Please answer with only facts based on the searched content.답변은 한국어로 번역해서 대답해줘';
  v_user_question   := p_user_question ;

  -- 질의내용에 대한 쿼리 벡터 생성
    SELECT TO_VECTOR(vector_embedding(onn.distiluse_base_multilingual_cased_v2 USING v_user_question as data)) as embedding into  v_user_question_vec ;  

           
  -- 텍스트 유사도 검색 (Multi-Vector Similarity Search)

select json_arrayagg(
         json_object('EMBED_ID' is EMBED_ID, 'EMBED_DATA' is EMBED_DATA) 
         returning CLOB) into v_message
from (
  SELECT EMBED_ID ,
       EMBED_DATA
  FROM DRE.rag_test
 ORDER BY VECTOR_DISTANCE(EMBED_VECTOR , v_user_question_vec, COSINE) 
 FETCH FIRST v_k_stop_count ROWS ONLY WITH TARGET ACCURACY 90
 );



  -- 프롬프트생성
  v_prompt := ' 
<s>[INST] {system_instruction}.

Search Data:
{insert_search_data_here}

User Query:
{user_query}

Provide a detailed response based on the search data above. [/INST]
  ';

  v_prompt := replace(replace(replace(v_prompt,'system_instruction',v_sys_instruction),'{insert_search_data_here}', v_message),'{user_query}',v_user_question);

  -- Return the substring of the CLOB within the size limit
  --  output := DBMS_LOB.SUBSTR(v_prompt, 4000, 1);
  utl_http.set_body_charset('UTF-8'); 
output := dbms_vector_chain.utl_to_generate_text(v_prompt, v_model_params);
  RETURN output;
END;
/

 

7. RAG 실행

위에서 만들 RAG 함수를 사용하여 Ollama 서버에 증강검색을 합니다. 

DECLARE
  p_user_question  CLOB := '마약류관리에관한법률위반(향정) 를 파기환송한 이유에 대해 알려줘';  -- 여기에 p_user_question의 값을 직접 입력
  output CLOB;
BEGIN
  output := DRE.fn_rag_test(p_user_question );
  DBMS_OUTPUT.PUT_LINE('Output: ' || output);
END;
/
-------------------------[Start Time: 2024/09/12 07:46:11]-------------------------
SQL> Output: 마약류관리에 관한 법률 위반(향정) 사건을 파기환송한 이유는 피고인 및 그와 공범관계에 있던 A의 사례로 볼 수 있습니다. 주된 쟁점은 검사 또는 사법경찰관 작성 피의자신문조서의 증거능력이었습니다.

아래는 이를 근거로 한 상세 설명입니다:

1. **검사 또는 사법경찰관 작성 피의자신문조서**: 피고인과 공범인 A에 대한 경우, 이들 작성한 신문조서가 법적으로 증거 능력을 가지는지 여부가 중요한 쟁점으로 다뤄졌습니다.
   
2. ***형사소송법 제312조***: 형사소송법 제312조는 검사 또는 경찰 작성의 피의자신문조서에 대한 규정을 담고 있으며, 이를 통하여 피고인이 진술거부권과 변호인 조력을 받을 권리를 보호하면서도 실질적인 진술 내용이 신뢰할 만한지 평가합니다.
   - 동 조항에 따르면 피의자나 피고인이 경찰이나 검찰 조사 과정에서 자백하거나 진술한 내용이 진정한 것인지를 판단하기 위해 몇 가지 요건들(예: 피고인의 자발성, 임의성)을 충족해야 합니다.

3. **원심 판결**: 원심(아래 단계 법원)은 이러한 형사 소송법 규정과 그 해석에 따라 본 사건에서 검사 또는 사법경찰관 작성 피의자신문조서를 적법하게 인정하고 유죄판결을 내렸습니다. 그러나 위에서 언급된 요건들이 충분히 충족되지 않았다고 판단하여 사후 증거능력이 문제가 되었고 이에 대한 상고가 제기됐습니다.
   
4. **대법원 판결**: 대법원은 원심의 판단이 부족함을 지적하며 사건을 상급심으로 돌려보냈습니다(즉, 파기환송). 대법원은 보다 면밀한 검토를 통해 실제 조사 과정에서 진술 강요 여부와 자발성 등에 대해 엄격히 재검토할 필요성이 있다고 보았던 것입니다.
   - 또한, 구체적인 사실관계 분석 및 관련 법령 적용에 있어서 추가 조사가 요구된 상황이었기 때문으로 추정됩니다.
 
따라서, 마약류관리에 관한 법률위반(향정)을 기소한 본 사건에서는 법원들 간 해석 차이와 이에 따른 심리 부족 등의 문제로 인해 최종 판단은 상급심인 대법원의 재검토 아래 다시 진행되게 된 것입니다.

 

마치며

테스트를 하면서  신기하다는 느낌은 받았지만, RAG를 활용하여 제품을 만들어서 활용하는 모습은 시간이 조금 걸릴것 같다라는 생각을 했습니다. 하지만, AI기술의 발전 속도를 보면 머지않아 우리 실생활에 빠질 수 없는 중요한 요소가 될 것이라고 생각이 듭니다.

ORACLE DB에서 AI를 활용하여 무엇을 할 수 있다는게 신기했습니다. 제가 테스트한 ORACLE 23AI FREE버전은 23.5인데 dbms_vector_chain.utl_to_generate_text 라는 함수를 사용하여 LLM쪽으로 질의를 하였습니다. 문제는 dbms_vector_chain.utl_to_generate_text 이 함수가 처음에는 VARCHAR2(4000)의 제한이 있었는데 현재 벡터서치 가이드에서는 CLOB로 변경된 것으로 보입니다. 
가이드에서처럼 Ollama Provider제공과 위 함수의 clob 지원이 되는 새로운 버전이 빨리 나왔으면 하는 바램입니다.

'ORACLE 23ai' 카테고리의 다른 글

Oracle Vector Index1-HNSW  (2) 2024.09.19
Oracle Embedding Model Load  (1) 2024.09.05
Embedding  (1) 2024.08.28
RAG(검색 증강 생성)이란  (0) 2024.08.26

들어가며

앞에 글들을 따라 오셨다면 ORACLE23 ai설치와 OML4Py 설치를 끝내셨을겁니다.
사전 학습된 오픈 소스 임베딩 모델이나 자체 임베딩 모델을 사용하여 Oracle Database 외부에서 벡터 임베딩을 생성할 수 있지만, Open Neural Network Exchange(ONNX) 표준과 호환되는 경우 해당 모델을 Oracle Database로 직접 가져올 수도 있습니다.
오라클에서 제공하는 머신러닝용 패키지인 OML4Py를 사용하여 ONNX 형식에 Embedding Model을 ORACLE DB에 로딩하는 방법을 알아보겠습니다.

 

Model Load

1.     모델을 관리할 스키마 생성

필요에 따라 모델을 관리할 스키마를 생성합니다. 저는 데이터 영역과 모델영역에 서비스 구분을 위해 ONN이라는 스키마를 생성하여 진행하겠습니다.

cd $ORACLE_HOME/oml4py/server
sqlplus / as sysdba
alter session set container=freepdb1;
CREATE TABLESPACE TS_ONN DATAFILE '/u01/oracle/FREE/FREEPDB1/ts_onn_1.dbf' SIZE 100M AUTOEXTEND ON NEXT 100M MAXSIZE 30G EXTENT MANAGEMENT LOCAL AUTOALLOCATE;
@pyquser.sql ONN TS_ONN TEMP unlimited pyqadmin

참고: 23ai부터 create tablespace bigfile default값입니다.
Starting with Oracle Database 23ai, BIGFILE functionality is the default for SYSAUX, SYSTEM, and USER tablespaces.

 

2.     DBDirectory object 생성

OS에 있는 Model을 DB안으로 가져오기 위해 Directory Object를 생성합니다.

sqlplus / as sysdba
alter session set container=freepdb1;
SQL> alter session set container=freepdb1;
grant read,write on directory MODEL_DIR to ONN;


3.     사전구성된 Model 확인

오라클에서 테스트한 모델을 확인 할 수 있습니다. Free버전이므로 아래에 모델 중 한국어 지원이 되는 'sentence-transformers/distiluse-base-multilingual-cased-v2' 모델을 사용하였습니다.

#명령어
python3
>>> from oml.utils import EmbeddingModel
EmbeddingModelConfig.show_preconfigured()
#결과
'sentence-transformers/all-mpnet-base-v2'
'sentence-transformers/all-MiniLM-L6-v2'
'sentence-transformers/multi-qa-MiniLM-L6-cos-v1'
'ProsusAI/finbert'
'medicalai/ClinicalBERT'
'sentence-transformers/distiluse-base-multilingual-cased-v2'
'sentence-transformers/all-MiniLM-L12-v2'
'BAAI/bge-small-en-v1.5'
'BAAI/bge-base-en-v1.5'
'taylorAI/bge-micro-v2'
'intfloat/e5-small-v2', 'intfloat/e5-base-v2'
'prajjwal1/bert-tiny'
'thenlper/gte-base'
'thenlper/gte-small'
'TaylorAI/gte-tiny'
'infgrad/stella-base-en-v2'
'sentence-transformers/paraphrase-multilingual-mpnet-base-v2'
'intfloat/multilingual-e5-base'
'intfloat/multilingual-e5-small'
'sentence-transformers/stsb-xlm-r-multilingual'

 

4.     ONNX형식 Embedding Model 다운로드

폐쇄망의 경우 파일을 직접 받아서 서버에 업로드 해야합니다. 업로드 위치는 생성한 디렉토리로 해주세요.

#명령어
python3
>>> from oml.utils import EmbeddingModel
>>> em = EmbeddingModel(model_name="sentence-transformers/distiluse-base-multilingual-cased-v2")
>>> em.export2file("distiluse-base-multilingual-cased-v2",output_dir="./")
/opt/oracle/product/23ai/dbhomeFree/python/lib/python3.12/site-packages/transformers/models/distilbert/modeling_distilbert.py:215: TracerWarning: torch.tensor results are registered as constants in the trace. You can safely ignore this warning if you use this function to create tensors out of constant variables that would be the same every time you call this function. In any other case, this might cause the trace to be incorrect.
  mask, torch.tensor(torch.finfo(scores.dtype).min)

 

5.     Embedding Model Load

임베딩 모델을 DB에 로드 합니다.

#명령어
sqlplus / as sysdba
alter session set container=freepdb1;

#모델디렉토리:MODEL_DIR
#모델파일:distiluse-base-multilingual-cased-v2.onnx
#스키마.모델명(원하는대로 입력가능):ONN.distiluse_base_multilingual_cased_v2
begin
  DBMS_VECTOR.LOAD_ONNX_MODEL('MODEL_DIR','distiluse-base-multilingual-cased-v2.onnx','ONN.distiluse_base_multilingual_cased_v2',
  JSON('{"function" : "embedding", "embeddingOutput" : "embedding", "input":{"input": ["DATA"]}}'));
end;

 

6.      Model Load 확인

select * from DBA_MINING_MODELS;

OWNER MODEL_NAME                           MINING_FUNCTION ALGORITHM ALGORITHM_TYPE CREATION_DATE       BUILD_DURATION  MODEL_SIZE   PARTITIONED
----- ------------------------------------ --------------- --------- -------------- ------------------- --------------- ------------ -----------
ONN   DISTILUSE_BASE_MULTILINGUAL_CASED_V2 EMBEDDING       ONNX      NATIVE         2024/08/09 17:17:47    0            540045499    NO

'ORACLE 23ai' 카테고리의 다른 글

Oracle Vector Index1-HNSW  (2) 2024.09.19
ORACLE 23ai RAG(증강검색) 실습  (4) 2024.09.19
Embedding  (1) 2024.08.28
RAG(검색 증강 생성)이란  (0) 2024.08.26

들어가며

오라클의 23ai Document 를 보고 정리한 내용입니다.
ORACLE Document URL :https://docs.oracle.com/en/database/oracle/oracle-database/23/vecse/index.html
23ai 공개 이후 AI에 대해 관심이 생겨 여러 자료들을 찾아보며 RAG(증강검색생성)까지 진행을 해봤습니다.
RAG를 할수록 원본 데이터의 품질이 중요하다고 느껴졌습니다. 
아직 청킹을 잘 하는 방법에 대해서는 이것저것 테스트를 해보고 있습니다.
우선 LLM과 RAG를 하기 전 데이터를 청킹하고 모델을 통해 임베딩을 하는 과정을 소개하려고 합니다.

 

 

Embedding이란?

Oracle AI Vector Search Oracle Database 내부 또는 외부의 비정형 데이터에서 벡터 임베딩을 자동으로 생성하는 Vector Utilities(SQL PL/SQL 도구)를 제공합니다.
벡터 임베딩 모델은 단어, 문장 또는 단락과 같은 데이터의 각 요소에 숫자 값을 할당하여 임베딩을 만듭니다.
데이터베이스 내에서 임베딩을 생성하려면 ONNX 형식의 벡터 임베딩 모델을 가져와 사용할 있습니다. 데이터베이스 외부에서 임베딩을 생성하려면 타사 REST API 호출하여 타사 벡터 임베딩 모델에 액세스할 있습니다.

 

데이터 변환 단계 이해

입력 데이터는 벡터로 변환되기 전에 여러 단계를 거칠 있습니다.

예를 들어, 텍스트 데이터(: PDF 문서) 일반 텍스트로 변환한 다음, 결과 텍스트를 작은 텍스트 조각(청크)으로 나누어 마지막으로 청크에 벡터 임베딩을 생성할 있습니다.

그림은 입력 데이터가 벡터로 변환되기 전에 통과하는 단계의 파이프라인을 보여줍니다.
1) PDF등의 텍스트를 Chunker 청크(Chunks) 생성하고,
2) 생성된 청크를 Tokenizer 토큰(Tokens)으로 생성하고,
( 별도로 Vocabulary 토큰이 처리되기 전에 Tokenizer 전달됩니다.)
3) 토큰이 Embedding Model 통해 벡터(Vector) 생성됩니다.
4) 마지막으로 Vector Vector Index 저장되는 것을 보여줍니다.

위의 데이터 변환 파이프라인을 데이터적인 관점에서 조금 알아보겠습니다.

파이프라인 또는 변환 체인(transformation chain) 단일 함수 또는 함수 조합을 포함할 있으며, 이는 소스 문서가 다른 표현(텍스트, 청크, 요약 또는 벡터)으로 변환될 적용됩니다.
체인 가능 유틸리티 함수는 데이터를 다른 표현으로 변환하는 특정 작업을 수행합니다. 예를 들어 데이터를 텍스트로 변환하거나, 텍스트를 청크로 변환하거나, 추출된 청크를 임베딩으로 변환하는 작업을 수행합니다. 

체인 가능한 유틸리티 함수는 유연하고 모듈화되도록 설계되었습니다.
그림에서 있듯이 파일-텍스트-청크-임베딩 체인은 다음 순서로 일련의 작업을 수행합니다.

     PDF 파일을 일반 텍스트로 변환합니다(UTL_TO_TEXT)
     결과 텍스트를 적절한 크기의 청크로 분할합니다(UTL_TO_CHUNKS)
     청크에 벡터 임베딩을 생성합니다(UTL_TO_EMBEDDINGS)

파일-텍스트-청크-임베딩 체인 쿼리를 통해 확인해보겠습니다.


파일을 일반 텍스트로 변환(UTL_TO_TEXT)

select 
dbms_vector_chain.utl_to_text(TO_BLOB(bfilename('DOC_DIR',a.file_name))) AS DOC_content_blob
from (values('대한민국 헌법.pdf')) a (file_name) ;

텍스트로 변환된 데이터를 청킹(UTL_TO_CHUNKS)

select t.*
  FROM (select 
dbms_vector_chain.utl_to_text(TO_BLOB(bfilename('DOC_DIR',a.file_name))) AS DOC_content_blob
from (values('대한민국 헌법.pdf')) a (file_name) ) dc 
CROSS JOIN TABLE (
                 dbms_vector_chain.utl_to_chunks(
                 dbms_vector_chain.utl_to_text(dc.DOC_CONTENT_BLOB)
       ,json('{"by":"characters","max":"3000","split":"none","normalize":"all","LANGUAGE":"KOREAN"}'))) t;


parameter :

  • BY:CHARACTERS(문자 수를 세어 나눔)
  • MAX:3000(각 청크의 최대 크기에 대한 제한을 지정합니다. 최대 크기 제한에 도달하면 데이터를 분할합니다.)
  • SPLIT:NONE(입력 텍스트가 최대 크기 제한에 도달했을 때 분할할 위치를 지정합니다. NONE은 한계 에서 분할하는 의미)
  • NORMALIZE:all(문서를 텍스트로 변환할 때 발생할 수 있는 문제(여러 개의 연속된 공백 및 smart quotes(굽은 따옴표(‘’,“”)) 등)를 자동으로 전처리하거나 후처리합니다. all=유니코드 구두점을 standard single-byte로 정규화합니다.)
  • LANGUAGE:KOREAN(언어는 한국어)


각 청크에 벡터 임베딩을 생성(UTL_TO_EMBEDDINGS)

select t.*
  FROM (select 
dbms_vector_chain.utl_to_text(TO_BLOB(bfilename('DOC_DIR',a.file_name))) AS DOC_content_blob
from (values('대한민국 헌법.pdf')) a (file_name) ) dc 
CROSS JOIN TABLE (
                 dbms_vector_chain.utl_to_embeddings(
                 dbms_vector_chain.utl_to_chunks(
                 dbms_vector_chain.utl_to_text(dc.DOC_CONTENT_BLOB)
                    ,json('{"by":"characters","max":"3000","split":"none","normalize":"all","LANGUAGE":"KOREAN"}')),
                        json('{"provider":"database", "model":"ONN.DISTILUSE_BASE_MULTILINGUAL_CASED_V2"}'))) t
CROSS JOIN JSON_TABLE(t.column_value, '$[*]' COLUMNS ( embed_vector CLOB PATH '$.embed_vector')) AS et;

 [: 제공되는 체인 가능한 유틸리티 함수]

기능 설명 입력 반환
UTL_TO_TEXT() 데이터(: Word, HTML 또는 PDF 문서)를 일반 텍스트로 변환합니다. CLOB 또는 BLOB로 입력을 허용합니다. 문서의 일반 텍스트 버전을 CLOB로 반환합니다
UTL_TO_CHUNKS() 데이터를 청크로 변환합니다. 입력을 일반 텍스트(CLOB 또는VARCHAR2)로 허용합니다.
데이터를 분할하여 청크 배열(CLOB)을 반환합니다.
UTL_TO_EMBEDDING() 데이터를 단일 임베딩으로 변환합니다. 입력을 일반 텍스트(CLOB)로 허용합니다. 단일 임베딩(VECTOR)을 반환합니다.
UTL_TO_EMBEDDINGS() 청크 배열을 임베딩 배열로 변환합니다. 입력을 청크 배열(VECTOR_ARRAY_T)로 허용합니다.
임베딩 배열(VECTOR_ARRAY_T)을 반환합니다.
UTL_TO_SUMMARY() 크거나 복잡한 문서와 같은 데이터에 대한 간결한 요약을 생성합니다. 입력을 일반 텍스트(CLOB)로 허용합니다. 일반 텍스트의 요약을 CLOB로 반환합니다.
UTL_TO_GENERATE_TEXT() 프롬프트에 대한 텍스트를 생성합니다. 입력을 텍스트 데이터(CLOB)로 허용합니다. 이 정보를 처리하여 생성된 텍스트가 포함된 CLOB를 반환합니다.

 

청크(Chunk) 작업

임베딩에서 알고리즘도 중요하지만 가장 중요한 것중 하나는 문서를 어떻게 파편으로 잘라낼것인가? (이를 영어로 Chunking이라고 한다.) 입니다.
오라클 데이터베이스는 텍스트를 분할을 위한 유틸리티 패키지인 DBMS_VECTOR_CHAIN 패키지를 제공합니다. 문서용 텍스트 데이터, 텍스트 데이터의 청크 분할, 콘텐츠 요약 임베딩을 모두 데이터베이스내에서 처리할수 있습니다.
청킹 기능은 VECTOR_CHAIN 패키지 VECTOR_CHUNKS 함수를 통해  사용이 가능합니다.
데이터를 일반 텍스트로 변환한 다음 결과 텍스트를 Chunker 전달합니다. 그런 다음 chunker chunking 이라는 프로세스를 사용하여 텍스트를 작은 청크로 나눕니다.
청킹에는 세부적인 파라미터들이 많아 아래에 표에 정리하였습니다.

VECTOR_CHUNKS parameters

Parameter Description and Acceptable Values
BY 데이터 분할 모드를 지정합니다. , 문자, 단어 또는 어휘 토큰의 수를 세어 분할합니다.
유효한 값 :
·       BY CHARACTERS(또는 BY CHARS):
문자 수를 세어 나눕니다.

·       BY WORDS:
단어의 개수를 세어 나눕니다.
단어는 알파벳 문자 시퀀스, 숫자 시퀀스, 개별 구두점 또는 기호로 정의됩니다. 공백 단어 경계가 없는 분할 언어(: 중국어, 일본어 또는 태국어)의 경우 각 모국어 문자는 단어(, 유니그램)로 간주됩니다.

·       BY VOCABULARY:
어휘 토큰의 개수를 세어 나눕니다.
어휘 토큰은 임베딩 모델이 사용하는 토크나이저의 어휘로 인식되는 단어 또는 단어 조각입니다. VECTOR_CHUNKS  helper API 사용하여 어휘 파일을 로드할 수 있습니다 DBMS_VECTOR_CHAIN.CREATE_VOCABULARY.
참고 : 정확한 결과를 얻으려면 선택한 모델이 청크에 사용된 어휘 파일과 일치하는지 확인하십시오. 어휘 파일을 사용하지 않는 경우 입력 길이가 모델의 토큰 제한 내에서 정의되었는지 확인하십시오.

기본값 :BY WORDS
MAX 각 청크의 최대 크기에 대한 제한을 지정합니다. 이 설정은 더 큰 텍스트에서 최대 제한이 발생하는 고정된 지점에서 입력 텍스트를 분할합니다. 단위는 모드 MAX에 해당합니다 BY. , 특정 수의 문자, 단어, 숫자, 구두점 또는 어휘 토큰의 최대 크기 제한에 도달하면 데이터를 분할합니다.
유효한 값 :
·       BY CHARACTERS: 50 to 4000 characters
·       BY WORDS: 10 to 1000 words
·       BY VOCABULARY: 10 to 1000 tokens
기본값 :MAX 100
 
SPLIT [BY]  
입력 텍스트가 최대 크기 제한에 도달했을 때 분할할 위치를 지정합니다. 이는 청크에 대한 적절한 경계를 정의하여 관련 데이터를 함께 유지하는 데 도움이 됩니다.

Valid values
:

1.    NONE:
MAX
문자, 단어 또는 어휘 토큰의 한계 에서 분할됩니다 .
 

2.    NEWLINE, BLANKLINE, and SPACE:
MAX
값 이전의 마지막 분할 문자에서 분할되는 단일 분할 문자 조건입니다.

NEWLINE을 사용하여 텍스트 행의 끝에서 분할합니다.
BLANKLINE
을 사용하여 공백 행의 끝(: 두 줄의 새 행)에서 분할합니다.
SPACE
를 사용하여 공백 행의 끝에서 분할합니다.

 
·       RECURSIVELY:
이것은 문자(또는 시퀀스)의 순서대로 나열된 목록을 사용하여 입력된 텍스트를 깨는 다중 분할 문자 조건입니다.

RASCERIESS는 다음 순서로 블랭크 라인, 새 라인, 스페이스, 없음으로 미리 정의됩니다:
1. 입력 텍스트가 MAX 값보다 크면 첫 번째 분할 문자로 분할합니다.
2. 실패할 경우 두 번째 분할 문자로 분할합니다.
3. 등등.
4. 분할 문자가 없는 경우 텍스트에 표시되는 곳마다 MAX로 분할합니다.
 

·       SENTENCE:
문장 경계에서 입력된 텍스트를 끊는 문장 끝 분할 조건입니다.
이 조건은 입력 언어의 문장 문장부호에 대한 지식과 문맥 규칙을 사용하여 문장 경계를 자동으로 결정합니다. 이 언어별 조건은 대부분 EOS(End-of-Sentence) 문장부호와 일반적인 약어에 의존합니다.
문맥 규칙은 단어 정보를 기반으로 하므로 이 조건은 텍스트를 단어 또는 어휘로 분할할 때만 유효합니다(not by characters).

Note:
이 조건은 BY WORD MAX 설정을 따르므로 경우에 따라 정확한 문장 경계를 결정할 수 없습니다. 예를 들어, 문장이 MAX 값보다 크면 MAX에서 문장을 분할합니다.
마찬가지로 MAX 한계 내에 맞는 경우에만 텍스트에 여러 문장을 포함합니다.
 

·       CUSTOM:
사용자 정의 분할 문자 목록에 따라 분할합니다. 각각 최대 10개의 길이로 최대 16개의 분할 문자 문자열로 사용자 정의 시퀀스를 제공할 수 있습니다.

다음과 같이 유효한 텍스트 리터럴을 제공합니다:
tab(\t), newline(\n), linefeed (\r)에 대해서만 시퀀스를 생략할 수 있습니다.
Default value: RECURSIVELY
OVERLAP 청크가 포함해야 할 이전 텍스트의 양(양의 정수 리터럴 또는 0)을 지정합니다. 이는 이전 청크 텍스트의 일부 양을 포함하여 관련 텍스트(: 문장)를 논리적으로 분할하는 데 도움이 됩니다.
중복의 양은 청크의 최대 크기를 측정하는 방법(문자, 단어 또는 어휘 토큰)에 따라 달라집니다. 중복은 지정된 SPLITE 조건(예를 들어 NEWLINE에서)에서 시작됩니다.
Valid value
: 5% to 20% of MAX

Default value: 0
LANGUAGE 입력 데이터의 언어를 지정합니다.
이 절은 특히 텍스트에 다른 언어로 다르게 해석될 수 있는 특정 문자(: 구두점 또는 약어)가 포함되어 있는 경우 중요합니다.
Valid value: Oracle Database Globalization Support Guide에 나열된 모든 NLS 지원 언어 이름 또는 언어 약어.
공백이 있는 모든 언어 이름에는 반드시 double quotation(")을 사용해야합니다.
For example:
LANGUAGE "simplified chinese"
 

한 단어 언어 이름의(one-word language names) 경우 quotation 표시가 필요하지 않습니다.
For example:

LANGUAGE american
 

Note: DBMS_VECTOR_CHAIN.CREATE_LANG_DATA chunker helper API를 사용하여 지정된 언어에 대해 언어별 데이터(약어 tokens)를 데이터베이스에 로드할 수 있습니다.
Default value: NLS_LANGUAGE from session
NORMALIZE 문서를 텍스트로 변환할 때 발생할 수 있는 문제(여러 개의 연속된 공백 및 smart quotes(굽은 따옴표(‘’, “”)) )를 자동으로 전처리하거나 후처리합니다. 오라클은 이 모드를 사용하여 품질이 좋은 청크를 추출하는 것을 권장합니다.
Valid values:
·       NONE:
정규화를 지정하지 않습니다.

·       ALL:
공통 다중 바이트(유니코드) 구두점을 standard single-byte로 정규화합니다.

·       PUNCTUATION:
smart quotes, smart hyphens 및 기타 단일 바이트 구두점과 동일한 멀티 바이트를 포함합니다.
For example:

o   2018u 'map to 0027'
o   2019u 'map to 0027'
o   201Bu 'map to 0027'
·       WHITESPACE:
불필요한 문자를 제거하여 공백을 최소화합니다.
For example, blanklines
은 유지하되, 추가로 newlines,spaces,tabs 는 제거합니다: " \n \n " => "\n\n"

·       WIDECHAR:
넓고 다중 바이트인 숫자와 (az) 문자를 단일 바이트로 정규화합니다.
0-9
a-z A-Z로 된 다중 바이트이며 , ZH/JA 형식 텍스트로 표시할 수 있습니다.

Note: 쉼표로 구분된 여백(WHITESPACE), 구두점(PUNCTUATION), and  WIDECHAR 괄호 목록(WIDECHAR in parentheses)을 반드시 지정해야합니다.
Default value: None
EXTENDED MAX_STRING_SIZE 매개 변수를 EXTED로 설정할 필요 없이 VARCHAR2 문자열의 출력 제한을 32767bytes로 늘립니다.
Default value: 4000 or 32767 (when MAX_STRING_SIZE=EXTENDED)

 


'ORACLE 23ai' 카테고리의 다른 글

Oracle Vector Index1-HNSW  (2) 2024.09.19
ORACLE 23ai RAG(증강검색) 실습  (4) 2024.09.19
Oracle Embedding Model Load  (1) 2024.09.05
RAG(검색 증강 생성)이란  (0) 2024.08.26

들어가며

AI 관련 테스트를 하다보면 오픈소스 라이브러리들을 많이 사용하게 되는데, 폐쇄망 서버에서는 제약사항들이 너무 많았습니다.
인터넷 환경의 경우 pip 를 통해서 설치가 매우 간단했는데, 사용할 수 없다보니 인터넷환경 테스트 로컬 테스트 서버를 구축하고 거기서 패키지들을 복사해서 서버에 옮긴 후 작업을 했습니다..

패키지별로 종속성이 얽혀있다보니 설치 순서가 중요하므로 설치 순서에 신경쓰면서 작업하세요.

 

PIP 패키지 다운로드

폐쇄망 서버에 설치하기 위해 VirtualBox로 구성한 인터넷 환경 로컬 테스트 서버에서 패키지들을 가져와서 옮겼습니다.

로컬 다운로드

  1. 다운로드 받을 디렉토리로 이동
  2. 이동한 폴더에서 CMD 실행
  3. pip3 download -d [다운로드위치] [패키지명]
패키지명 버전 다운로드 명령어 받은 파일명(종속성 정렬완료)
pip 24.2 pip3 download -d /media/sf_pip_package/pip pip pip-24.2-py3-none-any.whl
pandas 2.1.1 pip3 download -d /media/sf_pip_package/pandas pandas==2.1.1 numpy-2.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
six-1.16.0-py2.py3-none-any.whl
python_dateutil-2.9.0.post0-py2.py3-none-any.whl
pytz-2024.1-py2.py3-none-any.whl
tzdata-2024.1-py2.py3-none-any.whl
pandas-2.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
setuptools 68.0.0 pip3 download -d /media/sf_pip_package/setuptools setuptools==68.0.0 setuptools-68.0.0-py3-none-any.whl
scipy 1.12.0 pip3 download -d /media/sf_pip_package/scipy scipy==1.12.0 numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
scipy-1.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
matplotlib 3.8.4 pip3 download -d /media/sf_pip_package/matplotlib matplotlib==3.8.4 contourpy-1.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
cycler-0.12.1-py3-none-any.whl
fonttools-4.53.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl
kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
numpy-2.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
packaging-24.1-py3-none-any.whl
pillow-10.4.0-cp312-cp312-manylinux_2_28_x86_64.whl
pyparsing-3.1.2-py3-none-any.whl
python_dateutil-2.9.0.post0-py2.py3-none-any.whl
six-1.16.0-py2.py3-none-any.whl
matplotlib-3.8.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
oracledb 2.2.0 pip3 download -d /media/sf_pip_package/oracledb oracledb==2.2.0 cryptography-43.0.0-cp39-abi3-manylinux_2_28_x86_64.whl
cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
pycparser-2.22-py3-none-any.whl
oracledb-2.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
scikit-learn 1.4.1.post1 pip3 download -d /media/sf_pip_package/scikit-learn scikit-learn==1.4.1.post1 numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
scipy-1.14.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
joblib-1.4.2-py3-none-any.whl
threadpoolctl-3.5.0-py3-none-any.whl
scikit_learn-1.4.1.post1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
numpy 1.26.4 pip3 download -d /media/sf_pip_package/numpy numpy==1.26.4 numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
onnxruntime 1.17.0 pip3 download -d /media/sf_pip_package/onnxruntime onnxruntime==1.17.0 coloredlogs-15.0.1-py2.py3-none-any.whl
flatbuffers-24.3.25-py2.py3-none-any.whl
numpy-2.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
packaging-24.1-py3-none-any.whl
protobuf-5.27.3-cp38-abi3-manylinux2014_x86_64.whl
sympy-1.13.1-py3-none-any.whl
humanfriendly-10.0-py2.py3-none-any.whl
mpmath-1.3.0-py3-none-any.whl
onnxruntime-1.17.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
onnxruntime-extensions 0.10.1 pip3 download -d /media/sf_pip_package/onnxruntime-extensions onnxruntime-extensions==0.10.1 onnxruntime_extensions-0.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
onnx 1.16.0 pip3 download -d /media/sf_pip_package/onnx onnx==1.16.0 numpy-2.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
protobuf-5.27.3-cp38-abi3-manylinux2014_x86_64.whl
onnx-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
torch 2.2.0+cpu pip3 download -d /media/sf_pip_package/torch --extra-index-url https://download.pytorch.org/whl/cpu torch==2.2.0+cpu filelock-3.15.4-py3-none-any.whl
typing_extensions-4.12.2-py3-none-any.whl
sympy-1.13.1-py3-none-any.whl
networkx-3.3-py3-none-any.whl
jinja2-3.1.4-py3-none-any.whl
fsspec-2024.6.1-py3-none-any.whl
MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
mpmath-1.3.0-py3-none-any.whl
torch-2.2.0+cpu-cp312-cp312-linux_x86_64.whl
transformers 4.43.2 pip3 download -d /media/sf_pip_package/transformers transformers==4.43.2 filelock-3.15.4-py3-none-any.whl
huggingface_hub-0.24.5-py3-none-any.whl
numpy-2.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
packaging-24.1-py3-none-any.whl
PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
regex-2024.7.24-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
requests-2.32.3-py3-none-any.whl
tokenizers-0.19.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
safetensors-0.4.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
tqdm-4.66.4-py3-none-any.whl
fsspec-2024.6.1-py3-none-any.whl
typing_extensions-4.12.2-py3-none-any.whl
charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
idna-3.7-py3-none-any.whl
urllib3-2.2.2-py3-none-any.whl
certifi-2024.7.4-py3-none-any.whl
transformers-4.43.2-py3-none-any.whl
sentencepiece 0.2.0 pip3 download -d /media/sf_pip_package/sentencepiece sentencepiece==0.2.0 sentencepiece-0.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
joblib 1.2.0 pip3 download -d /media/sf_pip_package/joblib joblib==1.2.0 joblib-1.2.0-py3-none-any.whl


PIP 패키지 설치방법 안내

설치 방법에는 패키지 하나하나씩 설치하는 방법과 설치 목록을 텍스트 문서에 저장하여 설치하는 방법이 있습니다.

1. 개별설치

su - oracle
cd /u01/download/pip_package/
pip3 install --no-index pip-24.2-py3-none-any.whl
#실행로그
[oracle@devaidbv1 pip_package]$ pip3 install --no-index pip-24.2-py3-none-any.whl
Processing ./pip-24.2-py3-none-any.whl
Installing collected packages: pip
  Attempting uninstall: pip
    Found existing installation: pip 24.0
    Uninstalling pip-24.0:
      Successfully uninstalled pip-24.0
Successfully installed pip-24.2

 

2. 패키지별 one click 설치

설치를 위해서 종속성 순서 고려한 리스트 생성 텍스트 문서 생성를 해야합니다.
패키지별 텍스트 문서 내용은 게시글 맨 아래를 참고하세요.

예시) pandas-2.1.1_package.txt

txt 내용: 
반드시 종속성 순서에 맞게 넣을것!!
------------------------------------------------------------------------------------------------------------------------------------------------------------------

numpy-2.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
six-1.16.0-py2.py3-none-any.whl
python_dateutil-2.9.0.post0-py2.py3-none-any.whl
pytz-2024.1-py2.py3-none-any.whl
tzdata-2024.1-py2.py3-none-any.whl
pandas-2.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
------------------------------------------------------------------------------------------------------------------------------------------------------------------

 

 

OML4Py 패키지 설치

패키지 설치 확인 명령어 pip list | grep [패키지명]

  • oml4py 클라이언트용 : pip_package\oml4py_client
  • oml4py 서버용 : pip_package\oml4py_server

 

OML4Py Clinet 용

설치 패키지 버전 설치 명령어 one click 설치 종속성 순서
pip 24.2 pip3 install --no-index pip-24.2-py3-none-any.whl    
pandas 2.1.1 pip3 install --no-index numpy-2.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
pip3 install --no-index six-1.16.0-py2.py3-none-any.whl
pip
3 install --no-index python_dateutil-2.9.0.post0-py2.py3-none-any.whl
pip3 install --no-index pytz-2024.1-py2.py3-none-any.whl
pip
3 install --no-index tzdata-2024.1-py2.py3-none-any.whl

pip3 install --no-index pandas-2.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
pip3 install --no-index -r pandas-2.1.1_package.txt numpy>=1.26.0
six>=1.5
python-dateutil>=2.8.2
pytz>=2020.1
tzdata>=2022.1
setuptools 68.0.0 pip3 install --no-index setuptools-68.0.0-py3-none-any.whl    
scipy 1.12.0 pip3 install --no-index numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
pip
3 install --no-index scipy-1.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
pip3 install --no-index -r scipy-1.12.0_package.txt numpy<1.29.0,>=1.22.4
matplotlib 3.8.4 pip3 install --no-index contourpy-1.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
pip3 install --no-index cycler-0.12.1-py3-none-any.whl
pip3 install --no-index fonttools-4.53.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl
pip3 install --no-index kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
pip3 install --no-index numpy-2.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
pip3 install --no-index packaging-24.1-py3-none-any.whl
pip3 install --no-index matplotlib-3.8.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
pip3 install --no-index -r matplotlib-3.8.4_package.txt contourpy>=1.0.1
cycler>=0.10
fonttools>=4.22.0
kiwisolver>=1.3.1

numpy>=1.21
packaging>=20.0
pillow>=8
pyparsing>=2.3.1
python-dateutil>=2.7
six>=1.5
oracledb 2.2.0 pip3 install --no-index cryptography-43.0.0-cp39-abi3-manylinux_2_28_x86_64.whl
pip3 install --no-index cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
pip3 install --no-index pycparser-2.22-py3-none-any.whl
pip3 install --no-index oracledb-2.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
pip3 install --no-index -r oracledb-2.2.0_package.txt cryptography>=3.2.1
cffi>=1.12
pycparser
scikit-learn 1.4.1.post1 pip3 install --no-index numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
pip3 install --no-index scipy-1.14.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
pip3 install --no-index joblib-1.4.2-py3-none-any.whl
pip3 install --no-index threadpoolctl-3.5.0-py3-none-any.whl
pip3 install --no-index scikit_learn-1.4.1.post1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
pip3 install --no-index -r scikit-learn-1.4.1.post1_package.txt numpy<2.0,>=1.19.5
scipy>=1.6.0
joblib>=1.2.0
threadpoolctl>=2.0.0
numpy 1.26.4 pip3 install --no-index numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl    
onnxruntime 1.17.0 pip3 install --no-index coloredlogs-15.0.1-py2.py3-none-any.whl
pip3 install --no-index flatbuffers-24.3.25-py2.py3-none-any.whl
pip3 install --no-index numpy-2.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
pip3 install --no-index packaging-24.1-py3-none-any.whl
pip3 install --no-index protobuf-5.27.3-cp38-abi3-manylinux2014_x86_64.whl
pip3 install --no-index sympy-1.13.1-py3-none-any.whl
pip3 install --no-index humanfriendly-10.0-py2.py3-none-any.whl
pip3 install --no-index mpmath-1.3.0-py3-none-any.whl
pip3 install --no-index onnxruntime-1.17.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
pip3 install --no-index -r onnxruntime-1.17.0_package.txt coloredlogs
flatbuffers
numpy>=1.26.0
packaging

protobuf
sympy
humanfriendly>=9.1
mpmath<1.4,>=1.1.0
onnxruntime-extensions 0.10.1 pip3 install --no-index onnxruntime_extensions-0.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl    
onnx 1.16.0 pip3 install --no-index numpy-2.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
pip3 install --no-index protobuf-5.27.3-cp38-abi3-manylinux2014_x86_64.whl
pip3 install --no-index onnx-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
pip3 install --no-index -r onnx-1.16.0_package.txt numpy>=1.20
protobuf>=3.20.2
torch 2.2.0+cpu pip3 install --no-index filelock-3.15.4-py3-none-any.whl
pip3 install --no-index typing_extensions-4.12.2-py3-none-any.whl
pip3 install --no-index sympy-1.13.1-py3-none-any.whl
pip3 install --no-index networkx-3.3-py3-none-any.whl
pip3 install --no-index jinja2-3.1.4-py3-none-any.whl
pip3 install --no-index fsspec-2024.6.1-py3-none-any.whl
pip3 install --no-index MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
pip3 install --no-index mpmath-1.3.0-py3-none-any.whl
pip3 install --no-index torch-2.2.0+cpu-cp312-cp312-linux_x86_64.whl
pip3 install --no-index -r torch-2.2.0+cpu_package.txt filelock
typing-extensions>=4.8.0
sympy

networkx
jinja2
fsspec
MarkupSafe>=2.0

mpmath<1.4,>=1.1.0
transformers 4.43.2 pip3 install --no-index filelock-3.15.4-py3-none-any.whl
pip3 install --no-index huggingface_hub-0.24.5-py3-none-any.whl
pip3 install --no-index numpy-2.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
pip3 install --no-index packaging-24.1-py3-none-any.whl
pip3 install --no-index PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
pip3 install --no-index regex-2024.7.24-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
pip3 install --no-index requests-2.32.3-py3-none-any.whl
pip3 install --no-index tokenizers-0.19.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
pip3 install --no-index safetensors-0.4.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
pip3 install --no-index tqdm-4.66.4-py3-none-any.whl
pip3 install --no-index fsspec-2024.6.1-py3-none-any.whl
pip3 install --no-index typing_extensions-4.12.2-py3-none-any.whl
pip3 install --no-index charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
pip3 install --no-index idna-3.7-py3-none-any.whl
pip3 install --no-index urllib3-2.2.2-py3-none-any.whl
pip3 install --no-index certifi-2024.7.4-py3-none-any.whl
pip3 install --no-index transformers-4.43.2-py3-none-any.whl
pip3 install --no-index -r transformers-4.43.2.txt filelock
huggingface-hub<1.0,>=0.23.2
numpy>=1.17

packaging>=20.0
PyYAML>=5.1
regex!=2019.12.17
requests
tokenizers<0.20,>=0.19
safetensors>=0.4.1
tqdm>=4.27
fsspec>=2023.5.0
typing-extensions>=3.7.4.3
charset-normalizer<4,>=2
idna<4,>=2.5
urllib3<3,>=1.21.1
certifi>=2017.4.17
sentencepiece 0.2.0 pip3 install --no-index sentencepiece-0.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl    

 

OML4Py Server 용

다른 패키지들은 전부 설치가 되었는데 numpy-1.26.4 버전의 설치가 되지않아서 어쩔수 없이 정상적으로 설치된 서버 tar 가져와서 작업했습니다. numpy 설치 시 버전에 대한 업그레이드가 진행되지 않았습니다.
설치 에러는 아래와 같습니다.

트러블슈팅 -ValueError: numpy.dtype size changed 발생시 => oml4py 서버에 numpy==1.26.4 강제 업그레이드로 설치. 폐쇄망에서는 --target --upgrade to force 명령어 불가로 설치된 서버 oml4py디렉토리를 tar 가져옴

>>> res = oml.do_eval(func='TEST')

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "oml/embed/eval.py", line 112, in oml.embed.eval.do_eval
  File "oml/embed/eval1.py", line 257, in oml.embed.eval1.__eval
  File "/opt/oracle/product/23ai/dbhomeFree/python/lib/python3.12/site-packages/oracledb/cursor.py", line 701, in execute
    impl.execute(self)
  File "src/oracledb/impl/thick/cursor.pyx", line 306, in oracledb.thick_impl.ThickCursorImpl.execute
  File "src/oracledb/impl/thick/utils.pyx", line 456, in oracledb.thick_impl._raise_from_odpi
  File "src/oracledb/impl/thick/utils.pyx", line 446, in oracledb.thick_impl._raise_from_info
oracledb.exceptions.DatabaseError: ORA-20000: PyQuery error
ValueError: numpy.dtype size changed, may indicate binary incompatibility. Expected 96 from C header, got 88 from PyObject
ORA-06512: "PYQSYS.PYQ$EVALIMPL_IN",  77
ORA-06512: "PYQSYS.PYQ$EVALIMPL_IN",  74

가상화 서버
cd /opt/oracle/product/23ai/dbhomeFree
tar -cvf oml4py.par oml4py


설치중인 서버
cd /opt/oracle/product/23ai/dbhomeFree
rm -rf oml4py
tar -xvf oml4py.tar


 

OML4Py 패키지(One_click 설치용) 텍스트 파일 내용

패키지명 text file 명 text file 내용
pandas pandas-2.1.1_package.txt numpy-2.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
six-1.16.0-py2.py3-none-any.whl
python_dateutil-2.9.0.post0-py2.py3-none-any.whl 
pytz-2024.1-py2.py3-none-any.whl
tzdata-2024.1-py2.py3-none-any.whl
pandas-2.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
scipy scipy-1.12.0_package.txt numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
scipy-1.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
matplotlib matplotlib-3.8.4_package.txt contourpy-1.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
cycler-0.12.1-py3-none-any.whl
fonttools-4.53.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl
kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
numpy-2.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
packaging-24.1-py3-none-any.whl
pillow-10.4.0-cp312-cp312-manylinux_2_28_x86_64.whl
pyparsing-3.1.2-py3-none-any.whl
python_dateutil-2.9.0.post0-py2.py3-none-any.whl
six-1.16.0-py2.py3-none-any.whl
matplotlib-3.8.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
oracledb oracledb-2.2.0_package.txt cryptography-43.0.0-cp39-abi3-manylinux_2_28_x86_64.whl
cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
pycparser-2.22-py3-none-any.whl
oracledb-2.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
scikit-learn scikit-learn-1.4.1.post1_package.txt numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
scipy-1.14.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
joblib-1.4.2-py3-none-any.whl
threadpoolctl-3.5.0-py3-none-any.whl
scikit_learn-1.4.1.post1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
onnx onnx-1.16.0_package.txt numpy-2.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
protobuf-5.27.3-cp38-abi3-manylinux2014_x86_64.whl
onnx-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
torch torch-2.2.0+cpu_package.txt filelock-3.15.4-py3-none-any.whl
typing_extensions-4.12.2-py3-none-any.whl
sympy-1.13.1-py3-none-any.whl
networkx-3.3-py3-none-any.whl
jinja2-3.1.4-py3-none-any.whl
fsspec-2024.6.1-py3-none-any.whl
MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
mpmath-1.3.0-py3-none-any.whl
torch-2.2.0+cpu-cp312-cp312-linux_x86_64.whl
transformers transformers-4.43.2.txt filelock-3.15.4-py3-none-any.whl
huggingface_hub-0.24.5-py3-none-any.whl
numpy-2.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
packaging-24.1-py3-none-any.whl
PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
regex-2024.7.24-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
requests-2.32.3-py3-none-any.whl
tokenizers-0.19.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
safetensors-0.4.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
tqdm-4.66.4-py3-none-any.whl
fsspec-2024.6.1-py3-none-any.whl
typing_extensions-4.12.2-py3-none-any.whl
charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
idna-3.7-py3-none-any.whl
urllib3-2.2.2-py3-none-any.whl
certifi-2024.7.4-py3-none-any.whl
transformers-4.43.2-py3-none-any.whl

 

'ORACLE 23ai Install' 카테고리의 다른 글

OML4Py 설치_폐쇄망 서버  (0) 2024.08.27
ORALCE 23ai 설치  (0) 2024.08.27

OML4Py ?

머신러닝에서 가장 인기 있는 언어는 Python입니다.
파이썬은 풍부한 라이브러리(TensorFlow, Keras, Scikit-learn)와 단순한 문법 덕분에 머신러닝 연구와 개발에 매우 적합한 언어로 인식되고 있습니다.

오라클 DB에서 파이썬을 사용하기 위한 API OML4Py에 대해 간단하게 설명하겠습니다.

 

OML4Py(Oracle Machine Learning for Python)
Oracle Database Oracle Autonomous Database를 사용하여 데이터 탐색 및 준비, 머신 러닝 모델링, 솔루션 배포 등의 머신 러닝 프로세스를 지원하는 Python API입니다.

이번 ORACLE 23ai 에서 추가된 기능으로 사전 학습된 모델(Pretrainded Model) ONNX 형식으로 변환하여 사용할 수 있습니다.
OML4Py Hugging Face의 텍스트 변환기를 ONNX 형식 모델로 변환하여 사용할 수 있도록 합니다.
OML4Py는 또한 필요한 토큰화 및 후처리를 추가합니다.
그런 다음 결과 ONNX 파이프라인을 데이터베이스로 가져와 AI 벡터 검색을 위한 임베딩을 생성하는 데 사용할 수 있습니다

 

OML4Py 버전

설치에 앞서 OMP4Py는 아래와 같이 두 가지 버전이 존재합니다.

1. OML4Py Server : Python을 사용하여 데이터 분석 및 머신러닝 작업을 수행할 수 있게 해주는 서버 측 컴포넌트입니다. 주요기능은 다음과 같습니다.

  • 고성능 컴퓨팅: 대규모 데이터 처리와 복잡한 계산을 위해 데이터베이스의 컴퓨팅 자원을 활용합니다.
  • 데이터 이동 최소화: 데이터를 데이터베이스 내에 유지하여 네트워크를 통한 데이터 이동을 줄이고 성능을 최적화합니다.
  • 분산 처리: 여러 사용자와 작업이 동시에 실행될 수 있도록 지원합니다.

 

2. OML4Py Client : OML4Py 클라이언트는 사용자들이 로컬 환경에서 Python을 사용해 Oracle Database의 머신러닝 및 데이터 분석 기능에 접근할 수 있도록 도와주는 컴포넌트입니다.
주요 기능은 다음과 같습니다.

  • 로컬 작업 환경: 사용자가 친숙한 Python 인터페이스를 사용해 데이터 분석 및 머신러닝 작업을 수행할 수 있습니다.
  • 서버와의 통신 : OML4Py 서버와 연결하여 데이터베이스 내에서 고성능 연산을 실행하고 결과를 받아올 수 있습니다.
  • 통합 환경 : SQL Python을 통합하여 작업을 수행할 수 있습니다.

 

오라클이 서버와 클라이언트로 나눈 이유에 대해서는 아래와 같을 것이라고 생각이 됩니다

  • 자원 최적화: 서버는 고성능 하드웨어와 데이터베이스에 직접 접근하여 대규모 데이터를 효율적으로 처리하고, 클라이언트는 로컬 환경에서 사용자 친화적인 인터페이스를 제공합니다.
  • 분산 작업: 서버는 연산 집약적인 작업을 처리하고, 클라이언트는 이를 요청하고 결과를 받아보는 방식으로 작업을 분산하여 처리 효율을 높입니다.
  •  보안 및 관리: 서버 측에서 데이터를 중앙에서 관리하고, 클라이언트는 필요한 부분만 접근하도록 하여 보안과 관리 측면에서 유리합니다.

참고 :
23ai
의 경우 Python을 빌드하는 것은 클라이언트에만 필요합니다. 데이터베이스에는 Python 3.12.1이 있습니다 $ORACLE_HOME/python.
Hugging Face
의 텍스트 변환기를 ONNX 형식 모델로 변환하여 지원하는 기능은 OML4Py 클라이언트에서만 작동합니다. OML4Py 서버에서는 지원되지 않습니다.

 

OML4Py 설치

인터넷을 사용할 수 있는 환경에서는 ORACLE DOCUMENT를 따라하면 잘 설치가 되었기에 설치과정을 생략하고 폐쇄망 설치 내용만 정리했습니다.

폐쇄망 환경에서 PIP 를 자유롭게 사용할 수 없어 OML4Py 서버 설치 시 ORACLE 유저의 Python 패키지의 버전 차이로 오라클 엔진에서 필요한 패키지를 설치 할 수 없었기에, 클라이언트 설치를 먼저 진행하였습니다.

로컬에서 Oracle Python API OML4Py를 사용하기 위해 OML4Py 클라이언트 버전을 설치하겠습니다.
현재 저희 환경은 리눅스 서버이고,
클라이언트 환경 역시 설치된 리눅스 서버로진행하기 때문에 리눅스에 OML4Py Client를 설치하였습니다.

유저를 ORACLE이 아닌 새로운 유저로 생성 후 Python을 설치를 할 수도 있지만, Python 패키지 설치를 생략하려고 ORACLE_HOME에 설치된 Python을 사용하기 위해 ORACLE 유저에서 클라이언트 설치를 진행하였습니다.
(위에서 OML4Py 서버 설치시에 환경변수를 추가했습니다.)

 

OML4Py 클라이언트 설치 목차

  1. 환경변수 추가
  2. 라이브러리 설치
  3. 패키지 설치
  4. OML4Py 클라이언트 설치

 

OML4Py 클라이언트 설치

1)  환경변수추가
oracle 유저 환경변수에 python path를 추가합니다.

su – oracle 
vi .bash_profiel

export ORACLE_BASE=/opt/oracle
export ORACLE_HOME=$ORACLE_BASE/product/23ai/dbhomeFree
export ORACLE_SID=FREE
export NLS_LANG=KOREAN_KOREA.UTF8
export PYTHONHOME=$ORACLE_HOME/python
export PATH=$ORACLE_HOME/bin:$PYTHONHOME/bin:$PATH
export LD_LIBRARY_PATH=$PYTHONHOME/lib:$LD_LIBRARY_PATH

 

2)  라이브러리 설치

root 계정으로 클라이언트 머신 설치에 필요한 라이브러리를 설치합니다.

#명령어 #user:root
sudo yum install perl-Env libffi-devel openssl openssl-devel tk-devel xz-devel zlib-devel bzip2-devel readline-devel libuuid-devel ncurses-devel
#실행로그
[root@devaidbv1 ~]# sudo yum install perl-Env libffi-devel openssl openssl-devel tk-devel xz-devel zlib-devel bzip2-devel readline-devel libuuid-devel ncurses-devel
마지막 메타자료 만료확인(2:40:09 이전): 2024년 08월 01일 (목) 오후 12시 13분 20초.
꾸러미 openssl-1:1.1.1k-9.el8_7.x86_64가 이미 설치되어 있습니다.
꾸러미 openssl-devel-1:1.1.1k-9.el8_7.x86_64가 이미 설치되어 있습니다.
꾸러미 xz-devel-5.2.4-4.el8_6.x86_64가 이미 설치되어 있습니다.
꾸러미 zlib-devel-1.2.11-25.el8.x86_64가 이미 설치되어 있습니다.
종속성이 해결되었습니다.
==============================================================================================================================================
 꾸러미                                 구조                     버전                                   저장소                           크기
==============================================================================================================================================
설치 중:
 bzip2-devel                            x86_64                   1.0.6-26.el8                           BaseRepo                        224 k
 libffi-devel                           x86_64                   3.1-24.el8                             BaseRepo                         29 k
 libuuid-devel                          x86_64                   2.32.1-43.0.1.el8                      BaseRepo                         99 k
 ncurses-devel                          x86_64                   6.1-10.20180224.el8                    BaseRepo                        528 k
 perl-Env                               noarch                   1.04-395.el8                           AppstreamRepo                    21 k
 readline-devel                         x86_64                   7.0-10.el8                             BaseRepo                        204 k
 tk-devel                               x86_64                   1:8.6.8-1.el8                          AppstreamRepo                   498 k
종속 꾸러미 설치 중:
 expat-devel                            x86_64                   2.2.5-11.0.1.el8                       BaseRepo                         57 k
 fontconfig-devel                       x86_64                   2.13.1-4.el8                           BaseRepo                        151 k
 freetype-devel                         x86_64                   2.9.1-9.el8                            BaseRepo                        464 k
 libX11-devel                           x86_64                   1.6.8-6.el8                            AppstreamRepo                   976 k
 libXau-devel                           x86_64                   1.0.9-3.el8                            AppstreamRepo                    21 k
 libXft-devel                           x86_64                   2.3.3-1.el8                            AppstreamRepo                    25 k
 libXrender-devel                       x86_64                   0.9.10-7.el8                           AppstreamRepo                    22 k
 libpng-devel                           x86_64                   2:1.6.34-5.el8                         BaseRepo                        327 k
 libxcb-devel                           x86_64                   1.13.1-1.el8                           AppstreamRepo                   1.1 M
 ncurses-c++-libs                       x86_64                   6.1-10.20180224.el8                    BaseRepo                         58 k
 tcl-devel                              x86_64                   1:8.6.8-2.el8                          BaseRepo                        190 k
 tk                                     x86_64                   1:8.6.8-1.el8                          AppstreamRepo                   1.6 M
 xorg-x11-proto-devel                   noarch                   2020.1-3.el8                           AppstreamRepo                   280 k
 
연결 요약
==============================================================================================================================================
설치  20 꾸러미
 
전체 크기: 6.7 M
설치된 크기 : 17 M
진행할까요? [y/N]: y
꾸러미 내려받기 중:
연결 확인 실행 중
연결 확인에 성공했습니다.
연결 시험 실행 중
연결 시험에 성공했습니다.
연결 실행 중
  준비 중     :                                                                                                                           1/1 
  설치 중     : xorg-x11-proto-devel-2020.1-3.el8.noarch                                                                                 1/20 
  설치 중     : libXau-devel-1.0.9-3.el8.x86_64                                                                                          2/20 
  설치 중     : libxcb-devel-1.13.1-1.el8.x86_64                                                                                         3/20 
  설치 중     : libX11-devel-1.6.8-6.el8.x86_64                                                                                          4/20 
  설치 중     : libXrender-devel-0.9.10-7.el8.x86_64                                                                                     5/20 
  구현 중     : tk-1:8.6.8-1.el8.x86_64                                                                                                  6/20 
  설치 중     : tk-1:8.6.8-1.el8.x86_64                                                                                                  6/20 
  구현 중     : tk-1:8.6.8-1.el8.x86_64                                                                                                  6/20 
  설치 중     : tcl-devel-1:8.6.8-2.el8.x86_64                                                                                           7/20 
  설치 중     : ncurses-c++-libs-6.1-10.20180224.el8.x86_64                                                                              8/20 
  설치 중     : ncurses-devel-6.1-10.20180224.el8.x86_64                                                                                 9/20 
  설치 중     : libuuid-devel-2.32.1-43.0.1.el8.x86_64                                                                                  10/20 
  설치 중     : libpng-devel-2:1.6.34-5.el8.x86_64                                                                                      11/20 
  설치 중     : expat-devel-2.2.5-11.0.1.el8.x86_64                                                                                     12/20 
  설치 중     : bzip2-devel-1.0.6-26.el8.x86_64                                                                                         13/20 
  설치 중     : freetype-devel-2.9.1-9.el8.x86_64                                                                                       14/20 
  설치 중     : fontconfig-devel-2.13.1-4.el8.x86_64                                                                                    15/20 
  설치 중     : libXft-devel-2.3.3-1.el8.x86_64                                                                                         16/20 
  설치 중     : tk-devel-1:8.6.8-1.el8.x86_64                                                                                           17/20 
  설치 중     : readline-devel-7.0-10.el8.x86_64                                                                                        18/20 
  구현 중     : readline-devel-7.0-10.el8.x86_64                                                                                        18/20 
  설치 중     : perl-Env-1.04-395.el8.noarch                                                                                            19/20 
  설치 중     : libffi-devel-3.1-24.el8.x86_64                                                                                          20/20 
  구현 중     : libffi-devel-3.1-24.el8.x86_64                                                                                          20/20 
  확인 중     : bzip2-devel-1.0.6-26.el8.x86_64                                                                                          1/20 
  확인 중     : expat-devel-2.2.5-11.0.1.el8.x86_64                                                                                      2/20 
  확인 중     : fontconfig-devel-2.13.1-4.el8.x86_64                                                                                     3/20 
  확인 중     : freetype-devel-2.9.1-9.el8.x86_64                                                                                        4/20 
  확인 중     : libffi-devel-3.1-24.el8.x86_64                                                                                           5/20 
  확인 중     : libpng-devel-2:1.6.34-5.el8.x86_64                                                                                       6/20 
  확인 중     : libuuid-devel-2.32.1-43.0.1.el8.x86_64                                                                                   7/20 
  확인 중     : ncurses-c++-libs-6.1-10.20180224.el8.x86_64                                                                              8/20 
  확인 중     : ncurses-devel-6.1-10.20180224.el8.x86_64                                                                                 9/20 
  확인 중     : readline-devel-7.0-10.el8.x86_64                                                                                        10/20 
  확인 중     : tcl-devel-1:8.6.8-2.el8.x86_64                                                                                          11/20 
  확인 중     : libX11-devel-1.6.8-6.el8.x86_64                                                                                         12/20 
  확인 중     : libXau-devel-1.0.9-3.el8.x86_64                                                                                         13/20 
  확인 중     : libXft-devel-2.3.3-1.el8.x86_64                                                                                         14/20 
  확인 중     : libXrender-devel-0.9.10-7.el8.x86_64                                                                                    15/20 
  확인 중     : libxcb-devel-1.13.1-1.el8.x86_64                                                                                        16/20 
  확인 중     : perl-Env-1.04-395.el8.noarch                                                                                            17/20 
  확인 중     : tk-1:8.6.8-1.el8.x86_64                                                                                                 18/20 
  확인 중     : tk-devel-1:8.6.8-1.el8.x86_64                                                                                           19/20 
  확인 중     : xorg-x11-proto-devel-2020.1-3.el8.noarch                                                                                20/20 
 
설치되었습니다:
  bzip2-devel-1.0.6-26.el8.x86_64                   expat-devel-2.2.5-11.0.1.el8.x86_64            fontconfig-devel-2.13.1-4.el8.x86_64      
  freetype-devel-2.9.1-9.el8.x86_64                 libX11-devel-1.6.8-6.el8.x86_64                libXau-devel-1.0.9-3.el8.x86_64           
  libXft-devel-2.3.3-1.el8.x86_64                   libXrender-devel-0.9.10-7.el8.x86_64           libffi-devel-3.1-24.el8.x86_64            
  libpng-devel-2:1.6.34-5.el8.x86_64                libuuid-devel-2.32.1-43.0.1.el8.x86_64         libxcb-devel-1.13.1-1.el8.x86_64          
  ncurses-c++-libs-6.1-10.20180224.el8.x86_64       ncurses-devel-6.1-10.20180224.el8.x86_64       perl-Env-1.04-395.el8.noarch              
  readline-devel-7.0-10.el8.x86_64                  tcl-devel-1:8.6.8-2.el8.x86_64                 tk-1:8.6.8-1.el8.x86_64                   
  tk-devel-1:8.6.8-1.el8.x86_64                     xorg-x11-proto-devel-2020.1-3.el8.noarch      
 
완료되었습니다!

 

3)  파이썬 실행 경로 확인

파이썬에 접속해서 실행 위치를 확인합니다.
linux
서버 설치시 기본적으로 python이 설치되어 있어서 root에서도 python접속이 됩니다.
root
에 설치된 파이썬은 변경 할 수 없기 때문에 반드시 oracle 계정으로 접속해서 진행하세요. root python 버전은 3.6.8이고, oracle 유저의 python 버전은 3.12.3 입니다.

#명령어
su - oracle 
python3 
import sys
print(sys.executable)
#실행로그
[root@devaidbv1 ~]# su - oracle
[oracle@devaidbv1 ~]$ python3
Python 3.12.3 (main, Jul  3 2024, 08:35:38) [GCC 8.5.0 20210514 (Red Hat 8.5.0-18.0.6)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> print(sys.executable)
/opt/oracle/product/23ai/dbhomeFree/python/bin/python3

 

4) 클라이언트 머신에 필요한 패키지 설치

제가 설치한 환경은 폐쇄망 환경으로, 설치시 종속성에 따른 설치 순서가 중요했습니다.
따라서 패키지에 자세한 설치방법은 OML4Py server Requirements 글을 참고하세요.

트러블슈팅 :
Requirement already satisfied: numpy==1.26.4 in /opt/oracle/product/23ai/dbhomeFree/python/lib/python3.12/site-packages (1.26.4)
--upgrade to force 옵션으로 설치 필요. 무시하고 넘어가면 oml import test 시에 error발생
 

트러블슈팅 - pip3.12 install -U transformers 진행 하지 않을시에 ONNX 파일 생성시에 에러발생
FutureWarning: `resume_download` is deprecated and will be removed in version 1.0.0. Downloads always resume when possible.  => transformers 업데이트 진행
>>> em.export2file("multi-qa-MiniLM-L6-cos-v1",output_dir="./")
/opt/oracle/product/23ai/dbhomeFree/python/lib/python3.12/site-packages/huggingface_hub/file_download.py:1150: FutureWarning: `resume_download` is deprecated and will be removed in version 1.0.0. Downloads always resume when possible. If you want to force a new download, use `force_download=True`.
  warnings.warn(
pip install -U transformers

transformers 4.38.1일때 임베딩 모델 가져오는 부분에서 에러로 transformers-4.42.4으로 업그레이드해서 진행함.

5) 클라이언트 머신에 필요한 패키지 설치

oracle 계정으로 접속하여 설치된 패키지를 확인합니다.

#명령어 #user:oracle
python3
 
import numpy
import pandas
import scipy
import matplotlib
import oracledb
import sklearn
#실행로그
[oracle@localhost ~]$ python3
Python 3.12.1 (main, Feb  6 2024, 12:09:55) [GCC 8.5.0 20210514 (Red Hat 8.5.0-18.0.6)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy
>>> import pandas
>>> import scipy
>>> import matplotlib
>>> import oracledb
>>> import sklearn

 

6) Oracle Machine Learning for Python 클라이언트 설치

i) oml4py-client 엔진 다운로드

https://www.oracle.com/database/technologies/oml4py-downloads.html#license-lightbox 들어가서 다운로드 후
서버로 파일을 옮깁니다.

[oracle@localhost ~]$ ls -ltr oml4py-client-linux-x86_64-2.0.zip
-rw-r--r--. 1 oracle oinstall 8643185  7월 18 13:42 oml4py-client-linux-x86_64-2.0.zip


ii) Oracle Machine Learning for Python unzip
oml4py client 엔진에 압축을 해제합니다.

#명령어
unzip oml4py-client-linux-x86_64-2.0.zip
#실행로그
[oracle@localhost ~]$ unzip oml4py-client-linux-x86_64-2.0.zip
Archive:  oml4py-client-linux-x86_64-2.0.zip
   inflating: client/client.pl
   inflating: client/OML4PInstallShared.pm
   inflating: client/oml-2.0-cp312-cp312-linux_x86_64.whl
  extracting: client/oml4py.ver

 

iii) OML4Py Client 설치
클라이언트 설치 스크립트 실행합니다.(경로:oml4py 압축 해제한 디렉토리)

#명령어
perl -Iclient client/client.pl

* 트러블슈팅 -  Upgrade scikit_learn from version 2.0.1 to version 1.2.1 or higher at client/client.pl line 141
 => pip list | grep numpy  
버전이 1.26.4인지 확인  아닐시 1.26.4 설치

[oracle@devaidbv1 OMLP4y]$ perl -Iclient client/client.pl
Oracle Machine Learning for Python 2.0 Client.
Copyright (c) 2018, 2024 Oracle and/or its affiliates. All rights reserved.
Checking platform .................. Pass
Checking Python .................... Pass
Checking dependencies .............. /u01/download/OMLP4y/check_deps.py:2: DeprecationWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html
  from pkg_resources import WorkingSet, VersionConflict, DistributionNotFound
Fail
  ERROR: Upgrade scikit_learn from version 2.0.1 to version 1.2.1 or higher at client/client.pl line 141.

 

iv) OML모듈(OML4Py 클라이언트) 설치 확인

#명령어
python3
import oml
#실행로그
$ python3
Python 3.12.3 (main, Jul  3 2024, 08:35:38) [GCC 8.5.0 20210514 (Red Hat 8.5.0-18.0.6)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import oml
>>>

 

v) OML 경로 확인

['/opt/oracle/product/23ai/dbhomeFree/python/lib/python3.12/site-packages/oml'] 결과 값이 나오면 정상입니다.

#명령어
python3
import oml
oml.__path__
#실행로그
[oracle@devaidbv1 OMLP4y]$ python3
Python 3.12.3 (main, Jul  3 2024, 08:35:38) [GCC 8.5.0 20210514 (Red Hat 8.5.0-18.0.6)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import oml
>>> oml.__path__
['/opt/oracle/product/23ai/dbhomeFree/python/lib/python3.12/site-packages/oml']

 

 

OML4Py 서버 설치 목차

  1. 패키지 설치
  2. 필요 RPM 확인
  3. OML4Py 설치
  4. 구성확인

OML4Py 서버 설치

1) OML4Py 서버 머신에 필요한 패키지 설치

oracle 계정으로 접속하여 진행합니다.
참고로, oracle document pip3.12 install pandas 설치 쿼리에서 pandas==2.1.1 --target 부분에 띄워쓰기가 잘못되어 syntax error 나므로 --target 띄워져 있는지 확인하세요.
OML4Py server Requirements 문서에서 서버용 패키지 설치 방법을 참고하여 진행합니다.

 

2) 필요 RPM 확인

#명령어
rpm -qa perl-Env
#실행로그
[root@devaidbv1 download]# rpm -qa perl-Env
perl-Env-1.04-395.el8.noarch

 

 3) OML4Py 서버 설치


i. 디렉토리 이동

#명령어
su - oracle
cd $ORACLE_HOME/oml4py/server

 


ii. CDB$ROOT 로 접속

#명령어
sqlplus / as sysdba
show con_name
[oracle@devaidbv1 server]$ sqlplus / as sysdba
 
SQL> show con_name
CON_NAME
------------------------------
CDB$ROOT

 

iii. log 캡처를 위한 spool

SQL> spool oml4py_install_root.txt


iv. OML4Py 서버 설치를 위한 스크립트 실행

SQL> @pyqcfg.sql SYSAUX TEMP
SQL> spool off;

 

 

v. spool 한 파일을 열어 설치중에 발생한 오류가 있는지 확인

vi oml4py_install_root.txt

 

vi.PDB에서 OML4Py 설치 스크립트 실행

#명령어
sqlplus / as sysdba
show pdbs
spool oml4py_install_pdb.txt
alter session set container=FREEPDB1;
show con_name
@pyqcfg.sql SYSAUX TEMP
#실행로그
[oracle@localhost server]$ sqlplus / as sysdba
SQL*Plus: Release 23.0.0.0.0 - Production on 금 7월 26 10:04:43 2024
Version 23.4.0.24.05
 
Copyright (c) 1982, 2024, Oracle.  All rights reserved.
 
 
다음에 접속됨:
Oracle Database 23ai Free Release 23.0.0.0.0 - Develop, Learn, and Run for Free
Version 23.4.0.24.05
 
SQL> show pdbs
 
    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB$SEED                       READ ONLY  NO
         3 FREEPDB1                       READ WRITE NO
SQL> spool oml4py_install_pdb.txt
SQL> alter session set container=FREEPDB1;
 
세션이 변경되었습니다.
 
SQL> show con_name
 
CON_NAME
------------------------------
FREEPDB1
 
 
SQL> @pyqcfg.sql SYSAUX TEMP
 
spool off;

 

 

vii. 설치 로그를 열어 오류가 있는지 확인

vi oml4py_install_pdb.txt

 

4) 서버의 OML4Py 구성 확인

i. PDB 접속

#명령어
sqlplus / as sysdba
alter session set container=freepdb1;

ii.OML4Py 구성 확인

#명령어
select * from sys.pyq_config;

 

#실행로그
[oracle@devaidbv1 server]$ sqlplus / as sysdba
 SQL*Plus: Release 23.0.0.0.0 - for Oracle Cloud and Engineered Systems on 목 8월 1 18:49:53 2024
Version 23.5.0.24.07
 
Copyright (c) 1982, 2024, Oracle.  All rights reserved.
 
 
다음에 접속됨:
Oracle Database 23ai Free Release 23.0.0.0.0 - Develop, Learn, and Run for Free
Version 23.5.0.24.07
 
SQL> alter session set container=freepdb1;
 
세션이 변경되었습니다.
 
SQL> select * from sys.pyq_config;
 
NAME
--------------------------------------------------------------------------------
VALUE
--------------------------------------------------------------------------------
PYTHONHOME
/opt/oracle/product/23ai/dbhomeFree/python
 
PYTHONPATH
/opt/oracle/product/23ai/dbhomeFree/oml4py/modules
 
VERSION
2.0
 
 
NAME
--------------------------------------------------------------------------------
VALUE
--------------------------------------------------------------------------------
PLATFORM
ODB
 
DSWLIST
oml.*;pandas.*;numpy.*;matplotlib.*;sklearn.*

 

OML4Py 유저 생성

유저를 생성하는 방법에는 2가지가 있습니다.

일반 user 생성 쿼리로 생성 권한을 추가하는 법과, 오라클에서 제공하는 pyquser.sql 스크립트를 사용하는 방법이 있습니다.
저는 오라클에서 제공하는 쿼리로 유저를 생성했습니다.
user 생성 tablespace 생성은 스크립트 사용 유무와 상관없이 생성 진행해야 합니다.

1. Tablespace 생성

FREE 버전에서는 ORACLE_HOME /opt/ 밑으로 강제 되기에 저의 서버 환경에서는 root쪽 공간이 부족하여 /opt/oracle 심볼릭 링크로 /u01/opt/oracle 걸었습니다.
(root
에는 공간이 부족하여 2T 마운트  u01 심볼릭 링크)
따라서 테이블 스페이스 생성시에는 생성 위치를 /u01/oracle/FREE/FREEPDB1/ 으로 했습니다.

#명령어
CREATE TABLESPACE TS_ONN DATAFILE '/u01/oracle/FREE/FREEPDB1/ts_onn_1.dbf' SIZE 100M AUTOEXTEND ON NEXT 100M MAXSIZE 30G EXTENT MANAGEMENT LOCAL AUTOALLOCATE;

* 참고 : tablespace 삭제 쿼리 : drop tablespace TS_ONN including contents and datafiles;

 

2. 스크립트를 사용한 OML4Py 유저 생성

오라클에서 제공하는 쿼리를 사용하여 OML4Py가 사용할 유저를 생성합니다.

#명령어
@pyquser.sql onn TS_ONN TEMP unlimited pyqadmin
#실행로그
SQL> @pyquser.sql ONN TS_ONN TEMP unlimited pyqadmin
 
세션이 변경되었습니다.
 
password의 값을 입력하십시오: oracle
구   1: create user &&1 identified by &password
신   1: create user ONN identified by oracle
구   2: default tablespace &&2
신   2: default tablespace TS_ONN
구   3: temporary tablespace &&3
신   3: temporary tablespace TEMP
구   4: quota &&4 on &&2
신   4: quota unlimited on TS_ONN

사용자가 생성되었습니다.

구   4:     'create procedure, create mining model to &&1';
신   4:     'create procedure, create mining model to ONN';
구   6:   IF lower('&&5') = 'pyqadmin' THEN
신   6:   IF lower('pyqadmin') = 'pyqadmin' THEN
구   7:     execute immediate 'grant PYQADMIN to &&1';
신   7:     execute immediate 'grant PYQADMIN to ONN';

PL/SQL 처리가 정상적으로 완료되었습니다.


세션이 변경되었습니다.

 

OML4Py 연결 테스트

마지막으로 OML4Py가 잘 설치되었는지 확인하기 위해서 테스트를 하고 설치를 종료합니다.
host에 변수값을 환경에 맞게 변경하고 테스트 진행하세요.
실행로그에서 보이듯 res의 결과값이 2가 나오면 됩니다.

#명령어
python3
import oml
oml.connect(user='onn', password='oracle', host='xxx.xx.xxx.xx', port=1521, service_name='freepdb1')
oml.script.create("TEST", func='def func():return 1 + 1', overwrite=True)
res = oml.do_eval(func='TEST')
res
#실행로그
$ python3
Python 3.12.3 (main, Jul  3 2024, 08:35:38) [GCC 8.5.0 20210514 (Red Hat 8.5.0-18.0.6)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import oml
>>> oml.connect(user='onn', password='oracle', host='xxx.xx.xxx.xx', port=1521, service_name='freepdb1')
>>> oml.script.create("TEST", func='def func():return 1 + 1', overwrite=True)
>>> res = oml.do_eval(func='TEST')
>>> res
2

 

트러블슈팅 - oracledb.exceptions.DatabaseError: ORA-12541: 접속할 없습니다. host xx.xxx.xx.x port 1521 리스너가 없습니다.
·       => 리스너설정 참고하여 해결
       >>> oml.connect(user='onn', password='oracle', host='xx.xxx.xx.x', port=1521, service_name='freepdb1')

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "oml/core/methods.py", line 183, in oml.core.methods.connect
  File "/opt/oracle/product/23ai/dbhomeFree/python/lib/python3.12/site-packages/oracledb/connection.py", line 1158, in connect
    return conn_class(dsn=dsn, pool=pool, params=params, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/oracle/product/23ai/dbhomeFree/python/lib/python3.12/site-packages/oracledb/connection.py", line 544, in __init__
    impl.connect(params_impl, pool_impl)
  File "src/oracledb/impl/thick/connection.pyx", line 494, in oracledb.thick_impl.ThickConnImpl.connect
  File "src/oracledb/impl/thick/utils.pyx", line 446, in oracledb.thick_impl._raise_from_info
oracledb.exceptions.DatabaseError: ORA-12541: 접속할 수 없습니다. host xx.xxx.xx.x port 1521에 리스너가 없습니다.
도움말: https://docs.oracle.com/error-help/db/ora-12541/

 

'ORACLE 23ai Install' 카테고리의 다른 글

OML4Py Requirements Package  (0) 2024.08.27
ORALCE 23ai 설치  (0) 2024.08.27

+ Recent posts