CentOS7에 Tibero6 설치

티베로(Tibero)는 티맥스소프트의 자회사인 티맥스데이터에서 개발한 DBMS이다. 2003년에 처음 배포되었고, 현재 버전 6이 제공되고 있다. 이 글은 CentOS7에서 Tibero6을 설치하는 과정을 정리한 글이다.

다운로드 페이지에서 Tibero6을 Linux 64bits 용으로 다운로드 받는다. 실제 다운로드와 라이선스 발급을 위해 회원 가입이 필요하며 메일 인증을 통해 가입 처리가 이루어진다. 현재 이 글을 작성하는 시점을 기준으로 다운로드 받은 파일명은 tibero6-bin-FS07_CS_1912-linux64-174424-opt.tar.gz 이며, 이 파일을 설치하고자 하는 위치에 이동시킨 후 압축을 푼다. (필자의 경우 /etc/tibero 디렉토리로 하였고 압축을 풀면 tibero6이라는 하위 폴더가 생성되고 이 하위 폴더 안에 압축이 풀림)

tar -zxvf tibero6-bin-FS07_CS_1912-linux64-174424-opt.tar.gz

발급 받은 라이선스 파일은 설치된 디렉토리인 /etc/tibero의 하위 디렉토리인 /etc/tibero/tibero6/license에 복사한다. 참고로 라이선스 파일을 발급받기 위해서는 실치할 서버의 Host Name을 입력해야 하는데 리눅스의 경우 hostname 명령어를 입력해 쉽게 확인이 가능하다.

설치를 위한 좀 더 편리한 방법이 있을지도 모르겠으나, 티베로는 프로그램의 설치를 단순히 압축을 푸는 방식에서 시작해, 환경설정을 직접 콘솔에서 입력해 수행해 줘야 한다. 먼저 /etc/sysctl.conf의 파일에 다음의 내용을 추가한다.

kernel.shmmni = 4096
kernel.shmall = 2097152
kernel.shmmax = 2147483648
kernel.sem = 10000 32000 10000 10000

fs.file-max = 6815744

net.ipv4.ip_local_port_range = 1024 65500

/etc/security/limits.conf 파일에도 다음의 내용을 추가한다.

tibero           soft    nproc           2047
tibero           hard    nproc           16384
tibero           soft    nofile          1024
tibero           hard    nofile          65536

/etc/sysyemd/logind.conf 파일의 내용 중 RemoveIPC=no의 주석을 제거한다.

환경병수를 설정하기 위해 다음을 입력한다.

export TB_HOME=/etc/tibero/tibero6 
export TB_SID=tibero 
export LD_LIBRARY_PATH=$TB_HOME/lib:$TB_HOME/client/lib 
export PATH=$PATH:$TB_HOME/bin:$TB_HOME/client/bin

이제 티베로를 위해 이미 작성된 쉘을 실행한다.

$TB_HOME/config/gen_tip.sh

여기까지가 티베로 서버를 실해하기 위한 준비과정이며, 먼저 티베로 서버를 NOMOUNT 모드로 실행한다.

tbboot nomount

tbsql 프로그램을 통해 티베로 서버에 접속할 수 있으며, 아래처럼 sys 계정으로 초기 암호(tibero)를 지정해 접속한다.

tbsql sys/tibero

SQL> 프롬프트가 뜨면 성공한 것이고, 새로운 ‘tibero'(원한다면 다른 이름도 가능함)라는 이름의 데이터베이스를 생성한다.

create database "tibero" 
  user sys identified by tibero 
  maxinstances 8 
  maxdatafiles 100 character set MSWIN949 
  national character set UTF16 
  logfile 
    group 1 'log001.log' size 100M, 
    group 2 'log002.log' size 100M, 
    group 3 'log003.log' size 100M 
  maxloggroups 255 
  maxlogmembers 8 
  noarchivelog 
    datafile 'system001.dtf' size 100M autoextend on next 100M maxsize unlimited 
    default temporary tablespace TEMP 
      tempfile 'temp001.dtf' size 100M autoextend on next 100M maxsize unlimited 
      extent management local autoallocate 
    undo tablespace UNDO 
      datafile 'undo001.dtf' size 100M autoextend on next 100M maxsize unlimited 
      extent management local autoallocate;

데이터베이스 생성에는 다소 시간이 소요되며, 완료되면 quit를 입력해 tbsql을 종료하고, 티베로 서버를 NORMAL 모드로 실행한다.

tbboot

마지막으로 $TB_HOME/scripts의 system.sh를 실행한다. 실행과정 중 sys와 syscat의 암호를 입력해야 하는데, 각각 tibero와 syscat이다. 또한 중간 중간에 실행할 작업 수행 여부를 묻는데, 특별한 경우가 아닌한 y를 입력해 작업을 수행한다.

이제 티베로 서버가 정상적으로 수행되는지, 확인하기 위해 다음처럼 입력한다.

ps -ef | grep tbsvr

그 결과는 다음과 같다.

티베로 서버의 종료는 tbdown이며, 기본 Listener Port는 8629이다. 그리고 실제 데이터가 저장되는 데이터베이스의 위치는 설치된 티베로의 디렉토리를 기준으로 /etc/tibero/tibero6/database/{데이터베이스명}이다.

PHP 환경설정

/var/log/php-fpm에 로그 파일이 존재함

[로그파일 예시]
[11-Apr-2022 12:27:11] WARNING: [pool www] seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers), spawning 16 children, there are 3 idle, and 44 total children
[11-Apr-2022 12:27:12] WARNING: [pool www] seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers), spawning 32 children, there are 2 idle, and 46 total children
[11-Apr-2022 12:27:15] WARNING: [pool www] server reached pm.max_children setting (50), consider raising it

/etc/php-fpm.d 디렉토리의 www.conf 파일에 pm.max_children 설정값 등이 지정되어 있음

TensorFlow2를 이용한 간단한 회귀분석

TensorFlow v2가 정식버전으로 배포된지 몇달이 지났습니다. 필자는 딥러닝 라이브러리로 PyTorch를 주력으로 하고 있으나, TensorFlow로 만들어진 많은 코드 분석 및 협업을 위해 TensorFlow에 대한 API도 관심이 많습니다. 이 글에서는 TensorFlow 버전2에서 sin 함수에 대한 회귀분석에 대한 샘플 코드를 설명합니다.

필요한 패키지에 대한 import 및 훈련 데이터와 테스트 데이터를 아래 코드를 통해 준비합니다.

import numpy as np
import matplotlib.pyplot as plt
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.optimizers import SGD, RMSprop, Adam
from tensorflow.keras import metrics

np.random.seed(0)

train_x = np.linspace(0,np.pi*2,10).reshape(10,1)
train_y = np.sin(train_x)
test_x = np.linspace(0,np.pi*2,100).reshape(100,1)
test_y = np.sin(test_x)

sin 함수에 대한 회귀분석을 위한 신경망 모델은 다음과 같습니다.

sin 함수는 1개의 입력값을 받아 1개의 출력값을 가지므로 입력층과 출력층의 뉴런 개수는 1개입니다. 중간의 은닉층의 뉴런은 임의로 2개로 잡았습니다. 이 신경망 모델을 구성하는 코드는 다음과 같습니다.

input_nodes = 1
hidden_nodes = 2
output_nodes = 1

model = Sequential()

model.add(Dense(hidden_nodes, input_dim=input_nodes, activation='sigmoid'))
model.add(Dense(output_nodes))

print(model.summary())

모델 상세 정보가 콘솔에 표시되는데, 다음과 같습니다.

파라메터의 개수는 가중치 w 뿐만 아니라 편차값인 b 값도 고려해야 합니다.

신경망을 학습할 것인데, 학습에 사용할 최적화 방식으로 4가지를 사용합니다. 아래 코드에 사용할 최적화 방법에 대한 구체적인 코드는 다음과 같습니다.

optimizers = {
    'SGD': SGD(lr=0.1),
    'Momentum': SGD(lr=0.1, momentum=0.9), 
    'RMSProp': RMSprop(lr=0.01),
    'Adam': Adam(lr=0.01)
}

다음 코드는 학습입니다.

train_results = []
train_y_predicted = []
test_y_predicted = []

for optimizer_name, optimizer in optimizers.items():
    print(optimizer_name, 'Training ...')

    model.compile(optimizer=optimizer, loss='mean_squared_error', metrics=['mse'])

    result = model.fit(train_x, train_y, epochs=1000, verbose=0)
    train_results.append(result)

    train_result = model.predict(train_x)    
    train_y_predicted.append(train_result)

    test_result = model.predict(test_x)
    test_y_predicted.append(test_result)

위의 코드 중 8번의 model.compile은 모델 학습하기 위해 먼저 호출해야 하는 코드입니다. 인자로 loss와 metrics가 있는데, 각각 가질 수 있는 값은 ‘mean_squared_error'(주로 회귀용), ‘categorilcal_crossentropy'(주로 다중분류), binary_crossentropy'(주로 이진분류) 등과 ‘mse'(주로 회귀용), ‘accuracy'(주로 분류용) 등입니다. 그리고 실제 학습은 10번 코드를 통해 이뤄집니다. 단 1줄로 말입니다. 이 부분은 케라스의 장점이죠. model.fit 함수의 결과값은 손실값과 정확도에 대한 값을 포함합니다. 13번 코드와 16번 코드는 훈련된 모델을 통해 실제 계산을 수행하는 코드입니다. 각각 학습 데이터와 테스트 데이터로 계산을 수행해 그 결과를 배열에 담습니다.

앞의 코드에서는 중간 결과를 배열에 담았는데요. 이렇게 담은 배열은 최종 결과 그래프를 생성하기 위해 다음처럼 사용됩니다.

fig, axes = plt.subplots(3,1)
axes[0].plot(train_x, train_y, '-o', label = 'sin(x)')
for i, optimizer_name in enumerate(optimizers.keys()):
    axes[0].plot(train_x, train_y_predicted[i], '--', label=optimizer_name)
axes[0].legend()    

axes[1].plot(test_x, test_y, '-o', label = 'sin(x)')
for i, optimizer_name in enumerate(optimizers.keys()):
    axes[1].plot(test_x, test_y_predicted[i], '--', label=optimizer_name)
axes[1].legend()

for i, optimizer_name in enumerate(optimizers.keys()):
    axes[2].plot(train_results[i].history['loss'], '--', label=optimizer_name)
axes[2].legend()

plt.show()

결과는 다음과 같습니다.

이 경우 Momentum와 RMSprop의 장점을 섞은 Adam 최적화 방식이 가장 좋은 결과를 제공하는 것을 알 수 있습니다.

몬테카를로 방법(Montecarlo Method)

몬테카를로 방법(Monte Carlo method)은 어떤 문제에 대한 해를 무수히 많은 시도를 통해 얻어진 확률을 기반으로 하는 계산법입니다. 아래의 그림은 위키디피아의 몬테카를로에 대한 소개에 나온 이미지로써 원주율 π 값을 구하는 예입니다.

넓이가 1인 정사각형, 이 정사각형 내부에 반지름이 1인 사분원이 있습니다. 그러면 사분원이 차지하는 넓이는 π/4가 될 것이다. 이제 0 이상, 1 이하인 x와 y의 값을 무작위로 뽑은 후 x^2 + y^2 ≤ 1의 조건을 만족할 확률은 사분원의 넓이와 같은 π/4가 됩니다.

위의 논리를 코드로 작성하여 π를 구하면 다음과 같습니다.

import random

n = 1000000 # 백만번의 시도
count = 0

for i in range(n):
    # x, y를 무작위로 0~1사이의 값으로 결정
    x = random.uniform(0, 1)
    y = random.uniform(0, 1)

    # 사분원 내부에 발생하는 경우수 
    if (x**2 + y**2) <= 1: count += 1

# 백만번의 시도 중 사분원 내부일 경우에 대한 확률은 사분원의 넓이이므로 이를 4배 곱하여 π 계산
print('phi', 4*count/n)

위의 코드 중 4*count/n은 다음의 비례식을 통해 도출된 결과입니다.

전체확률 : 사분원 내부일 경우에 대한 확률 = 사각형의 넓이 : 사분원의 넓이

위의 비례식에 수치값을 대입하면 다음과 같습니다.

1 : count/n = 1 : π/4

몬테카를로 방법을 통해 실제와 가까운 해를 얻기 위해서는 방대한 단순 계산을 매우 빠르게 처리할 수 있는 컴퓨터가 필수입니다. 이 몬테카를로 방법은 핵폭탄이나 수소폭탄의 개발에서 핵심적인 역활을 담당했다고 합니다. 제 경우도 핵폭탄 개발이 필요해서... 가 아닌 강화학습(Reinforcement learning)의 한 방법으로 접하게 되었습니다.

FingerEyes-Xr에서 속성값으로 도형 심벌 및 라벨문자열 설정하기

여러 개의 속성값으로 라벨 문자열을 조립하여 실제 표시되는 라벨을 결정하는 코드의 예입니다.

CustomLabelFormatter = Xr.Class({
    name: "CustomLabelFormatter",
    extend: Xr.label.ProgrammableLabelFormatter,
    requires: [Xr.label.ILabelFormatter],
    construct: function (layer) {
        this.superclass(layer);
        this._MGN_CD = -1;
        this._MGN_DT = -1;
        this._codeValues = {
            'C01': '시설A',
            'C02': '시설B',
            'P01': '시설C',
            'P03': '시설D',
            'R03': '시설E',
            'T01': '시설F'
        };
    },
    methods: {
        value: function (shapeRow, fieldSet, attributeRow) {
            if (this._EQMT_FIXPLC_RGN_SE_CD == -1) {
                this._EQMT_FIXPLC_RGN_SE_CD = fieldSet.fieldIndex("MGN_CD");
            }

            if (this._FST_REGI_TSP == -1) {
                this._FST_REGI_TSP = fieldSet.fieldIndex("_MGN_DT");
            }

            let code = attributeRow.valueAsString(this._MGN_CD);

            // 필드 2개의 값(_MGN_CD 필드의 코드값 + _MGN_DT 의 값)을 조합한 라벨 표시, 예: 시설B(2020-07-27 14:32:32)
            return this._codeValues[code] + "(" + attributeRow.valueAsString(this._MGN_DT) + ")";
        }
    }
});

var lyr = new Xr.layers.ShapeMapLayer("lyr", ...);
let label = lyr.label();
label.enable(true);

let formatter = new CustomLabelFormatter(lyr);
label.formatter(formatter);

lm.add(lyr);

다음은 속성값으로 도형의 스타일 심벌을 지정하는 코드의 예입니다.

CustomLayerTheme = Xr.Class({
    name: "CustomLayerTheme",
    extend: Xr.theme.ProgrammableShapeDrawTheme,
    requires: [Xr.theme.IShapeDrawTheme],
    construct: function (/* ShapeMapLayer */ layer) {
        this.superclass(layer);
        this._fieldIndex = -1;
        let codes = ['R01', 'P02', 'R03', 'P01', 'P02', 'T01', 'T02', 'C01', 'C02'];
        let colors = ['#f1c40f', '#f39c12', '#e67e22', '#e74c3c', '#c0392b', '#ff0000', '#00ff00', '#0000ff', '#ff00ff'];
        let symbols = [];
        let cntCodes = codes.length;
        for (let i = 0; i < cntCodes; i++) {
            let SDS = new Xr.symbol.ShapeDrawSymbol();
            SDS.brushSymbol().color(colors[i]).opacity(0.5);
            SDS.penSymbol().color(colors[i]).width(2);

            let symbol = {
                code: codes[i],
                symbol: SDS
            };

            symbols[i] = symbol;
        }

        this._symbols = symbols;
    },
    methods: {
/* ShapeDrawSymbol */ symbol: function (/* ShapeRow */ shapeRow, /* FieldSet */ fieldSet, /* AttributeRow */ attributeRow) {
            if (this._fieldIndex === -1) {
                this._fieldIndex = fieldSet.fieldIndex("MGN_CD");
            }

            let value = attributeRow.valueAsString(this._fieldIndex);
            let symbols = this._symbols;
            let symbol = undefined;
            let cntSymbols = symbols.length;
            for (var i = 0; i < cntSymbols; i++) {
                symbol = symbols[i];
                if (value === symbol.code) {
                    break;
                }
            }
            return symbol.symbol;
        },

/* boolean */ needAttribute: function () {
            return true;
        }
    }
});


var lyr = new Xr.layers.ShapeMapLayer("lyr", ...);

var newTheme = new CustomLayerTheme(lyr)
lyr.theme(newTheme);

lm.add(lyr);