이번에 테스트를 한번 해봤는데 내장 함수들에 너무 의존적이라는 것을 알게 되어서 알고리즘 글을 올리게 되었습니다.

 

\(a^{n} = a * a * a ...\)

 

먼저 가장 쉬운 제곱 알고리즘의 경우는 아래와 같습니다.

 

int n = 10;
int num = 3;
int answer = 1;

for (int i = 0; i < n; i++)
{
	answer *= num;
}

cout << answer << endl;
// 59049

 

위와 같은 알고리즘은 지수 n번만큼 num을 곱해주게 되므로 시간복잡도는 O(n)이 됩니다.

 

하지만 더욱 효과적인 O(logn) 방법이 있다는걸 이번에 알게 되었습니다.

 

int n = 10;
int num = 3;
int answer = 1;

while (n)
{
	if (n & 1)
	{
		answer *= num;
	}
	n = n >> 1;
	num *= num;
}
cout << answer << endl;
// 59049

 

지수 10은 2진수로 1010(2)와 같이 표현되고

 

1010(2) = 1000(2) + 10(2) = 2^3 + 2^1 로도 표현할 수 있습니다.

 

비트연산자로 1010(2)를 1과 and 연산을 해주어 0일 경우,

 

비트를 오른쪽으로 옮겨주면서 숫자 num을 제곱해줍니다.

 

1일 경우에는 answer에 num을 곱해주고 숫자 num을 제곱해줍니다.

 

No 지수 n = 1010(2) num = 3^1 answer = 1
1 101(2) 3^2 1
2 10(2) 3^4 3^2
3 1(2) 3^8 3^2 * 3^8

 

그렇다면 총 10번을 곱해줘야 할 계산을 4번만에 해결할 수 있게 됩니다.

 

 

딥러닝 기반의 얼굴 랜드마크 검출 모델

  • heatmap regression
    • 좋은 성능을 보임
    • 연산이 비싸고, outlier에 민감함
  • coordinate regression
    • 빠르고 robust
    • 정확도가 충분치 않음

다양한 환경에서 사용할 수 있고 효율적인 facial landmark detector인 PIP-Net(Pixel-In-Pixel Net) 제안

  • three essential part
    • Pixel-In-Pixel regression
    • a neighbor regression module
    • self-training with curriculum

PIP Regression

face landmark detector

  • coordinate regression
    • output이 길이 2N의 FC layer
    • N : represents the number of landmarks
  • heatmap regression
    • input과 같은 해상도를 가지도록 추출된 feature map을 upsampling
    • output heatmap은 N의 채널을 가짐
      • 각각은 해당 랜드마크 위치의 가능성 반영
  • coordinate regression이 연산 면에서 높은 효율성을 보이나, heatmap regression이 정확도 면에서 좋은 성능을 보임
    • 연산의 비효율성에도 불구하고, heatmap 방식은 SOAT 정확도를 달성
  • 두 방식의 장점만을 가진 PIP regression 방식 제안
    • heatmap regression 기반
    • upsampling layer는 feature map에 point를 위치 시키는데 필요하지 않음
      • low-resolution layer만으로도 localization하는데 충분
      • low-resolution feature map을 적용함으로서 각 랜드마크에 대한 가장 가능성 높은 grid를 얻음
      • grid의 왼쪽 상단 모서리를 기준으로 x축과 y축의 각 heatmap grid 내에서 오프셋 예측도 적용
      • 점수와 오프셋 예측은 서로 독립적이므로 병렬로 계산 가능(single-stage method)

 

Neighbor Regression Module

  • PIP-Regression은 Heatmap Regression의 효율성 문제를 해결하지만, 여전히 robust하지 못함

  • 일관된 landmark 예측을 하는데 도움을 줄 수 있는 NRM(Neighbor Regression Module) 모듈 제안
    • landmark offset 외에도 각 랜드마크는 C개의 neighbor offset을 predict
    • NRM은 크기가 2CN × HM ×WM인 Neighbor map을 추가로 출력
    • Face landmark의 mean shape은 학습 데이터의 ground-truth를 사용하여 계산하고 대상 랜드마크와 가장 가까운 C개의 랜드마크를 neighbor로 정의

 

Self-Training with Curriculum

  • NRM은 PIP Regression의 불안정성을 완화하지만, crossdomain dataset에는 적합하지 않음
  • 도메인 간 일반화 개선을 위해 레이블이 지정되지 않은 데이터를 추가로 사용하는 Self-Training with Curriculum 제안
    • task의 난이도가 점차 증가하여 인간이 학습하는 방식을 모방
    • task level에서 커리큘럼 학습 적용
    • Pipeline of STC
      1. 수동으로 레이블된 data로 일반적인 방식으로 PIPNet 학습
      2. 학습된 detector를 통해 레이블 되지 않은 data터의 pseudo-label을 추정
      3. 수동으로 레이블된 data와 pseudo-label된 data로 새로운 학습 dataset 생성
      4. 새로운 dataset을 이용하여 PIPNet 학습
    • 모델이 수렴할 때 까지 2~4 task 반복

'Deep Learning > Vision' 카테고리의 다른 글

VieWorks 카메라 셋팅  (0) 2023.07.17
FaceBoxes 논문 정리  (0) 2023.02.03
06. Vision Transform (작성중)  (0) 2022.05.13
05. Yolo 버전별 비교  (0) 2022.05.13
04. Faster RCNN [Vision, Object Detection]  (0) 2022.02.09
  • Faster R-CNN의 RPN과 SSD의 multi-scale mechanism에 영향을 받음
  • Three Contributions
    • RDCL(Rapidly Digested Convolutional Layers)
    • MSCL(Multiple Scale Convolutional Layers)
    • Anchor Densification Strategy

RDCL(Rapidly Digested Convolutional Layers)

  • Shrinking the spatial size of input
    • input에 들어오는 spatial size를 줄이기 위해 convolution layer와 pooling layer에 큰 값의 stride size 사용
    • Conv1, Pool1, Conv2, Pool2 = 4, 2, 2, 2
    • RDCL에서 총 32의 stride 사이즈 사용
      • spatial size가 32배 빠르게 축소됨
  • Choosing suitable kernel size
    • 초반 레이어들의 kernel size는 스피드를 위해 빨라야 하고, 공간 크기 감소로 인한 정보 손실을 완화하기에 충분히 커야 함
    • Conv1, Conv2, all Pool = 7x7, 5x5, 3x3
  • Reducing the number of output channels
    • C.ReLU activation function을 활용하여 output channels의 수를 줄인다.
      • C.ReLU는 output channel을 doubling하는 기능은 있지만, 채널 수 감소에 대한 내용은 없음.
      • C.ReLU은 추가적인 정보 제공을 통해 모델 parameter 수를 잠재적으로 줄일 수 있음.(같은 성능을 위해선 더 많은 파라미터 수가 필요하기 때문)
      • 채널 수를 줄인다는 내용에 대해선 추가적인 확인 필요[Link]

MSCL(Multiple Scale Convolutional Layers)

RPN 기반의 method

RPN은 단일 클래스(ex. 얼굴)에서는 자연스러운 검출기 역할을 하나, 독립적인 얼굴 검출기로서는 성능이 좋지 않음. 이에 대한 두 가지 요소로는

  • RPN의 anchor는 마지막 convolution layer 와만 연관되어 있음
    • 마지막 레이어의 feature과 해상도는 다양한 크기의 얼굴을 처리하기 약함
  • anchor에 관련된 레이어는 하나의 receptive field만을 가지며, 다른 크기의 얼굴과 일치할 수 없음

위 두 가지 문제를 해결하기 위해 두 가지 차원에 따라 설계

  • Multi-scale design along the dimension of network depth
    • default anchor들은 Inception3, Conv3_2, Conv4_2와 같은 multi-scale feature map과 연관
    • 위 레이어들은 네트워크 깊이의 차원에 따른 다중 스케일로 설계
    • 다양한 크기의 면을 자연스럽게 처리하기 위해 서로 다른 해상도로 여러 레이어에 걸쳐 앵커를 이산화
  • Multi-scale design along the dimension of network width
    • anchor와 관련 레이어의 출력 특성이 다양한 크기의 수용 필드에 해당해야 함(다양한 크기의 얼굴을 학습하기 위함)
      • 네트워크 폭 차원을 따라 multi-scale design된 Inception 모듈을 통해 충족
Inception Module

 

Anchor Densification Strategy

  • image에서 tiling interval of anchor(Ainterval)는 anchor와 연관된 레이어의 stride size와 같음
  • scale of the anchor(Ascale)은 face box가 거의 정사각형에 가깝기 때문에 1:1의 비율을 가짐
  • Ainterval 와 Ascale 을 통해 tiling density of anchor(Adensity)를 계산
  • Ascale : 32, 64, 128, 256, 512
  • Ainterval : 32, 32, 32, 64, 128
  • Adensity : 1, 2, 4, 4, 4
    • scale이 큰 anchor와 비교해서 작은 anchor에서 작은 얼굴에 대한 recall rate가 현저히 낮음
    • 이 imbalance를 해결하기 위해 Anchor Densification Strategy 제안

 
  • 한 가지 유형의 anchor를 n번 고밀도화 하기 위해, 하나의 receptive field의 중심에 한번만 tiling 하는 대신 하나의 receptive field 중심 주위에 Anumber = n2 개의 anchor를 균일하게 tiling

Training

  • Training Dataset : WIDER FACE(12880 image)
  • Data Augmentation
    • Color distortion
    • Random Cropping
    • Scale Transformation
    • Horizontal Flipping
    • Face-box filter
  • Matching Strategy
    • 어떤 anchor를 face bounding box에 대응시킬 지 결정
      1. 각 얼굴을 best jaccard overlap을 가지는 anchor와 매칭
      2. threshold(0.35)보다 높은 jaccard overlap을 가지는 anchor에 매칭

  • Loss Function
    • Faster R-CNN 에 있는 RPN과 같은 loss funtion 사용
    • 2 class softmax loss for clasification, smooth L1 loss for regression
  • Hard negative mining
    • anchor 매칭을 진행한 후, 대부분의 anchor는 negative(significant imbalance)
    • 빠른 최적화와 안정적인 학습을 위해, loss값으로 정렬 후 top one을 고름
      • negative : positive = 3:1
  • Other implementation details
    • 모든 파라미터들은 xavier 방식으로 랜덤하게 초기화됨
    • 결과 모델을 파인튜닝
      • SGD with 0.9 momentum
      • 0.0005 weight decay
      • batch size 32
      • maximun iterations 120k
        • 10-3 lr for first 80k iters
        • 10-4, 10-5 for 20k iters

'Deep Learning > Vision' 카테고리의 다른 글

VieWorks 카메라 셋팅  (0) 2023.07.17
PIP-Net 논문 정리  (0) 2023.02.16
06. Vision Transform (작성중)  (0) 2022.05.13
05. Yolo 버전별 비교  (0) 2022.05.13
04. Faster RCNN [Vision, Object Detection]  (0) 2022.02.09

헤더 파일

#pragma once
#ifndef __TORCH_CLASSIFICATION_H__
#define __TORCH_CLASSIFICATION_H__

#include <opencv2/opencv.hpp>
#include <iostream>

// 설정 파일
#include "config.h"

// 이미지 처리
#include "img_utils.h"

// dlib
#include "dlib_utils.h"

// pytorch
#include "torch/torch.h"
#include "torch/script.h"

class EmoTorch
{
private:
	static void EmoTorch::torch_model_load(torch::jit::script::Module& module);
	static int EmoTorch::torch_predict(cv::Mat frame, torch::jit::script::Module& model);
public:
	static int torch_process();
};

#endif

 

CPP 파일

#include "torch_classification.h"

void EmoTorch::torch_model_load(torch::jit::script::Module& module)
{
	try {
		// Load Model
		module = torch::jit::load(TORCH_MODEL_PATH);

		// Convert to Device
		torch::Device device(torch::kCPU, 0);
		module.to(at::kCPU);

		return;
	}
	catch (const c10::Error& e) {
		std::cerr << "[error 02]" << std::endl;
		std::cerr << "error loading the pytorch model" << std::endl;
	}
	return;
}

int EmoTorch::torch_predict(cv::Mat frame, torch::jit::script::Module& model)
{
	// Using Model
	cv::cvtColor(frame, frame, cv::COLOR_BGR2RGB);
	frame.convertTo(frame, CV_32FC1);
	torch::Tensor tensor_image = torch::from_blob(frame.data, { 1, IMG_SIZE, IMG_SIZE, 3 });
	tensor_image = tensor_image.permute({ 0, 3, 1, 2 });
	//tensor_image[0][0] = tensor_image[0][0].sub_(0.485).div_(0.229);
	//tensor_image[0][1] = tensor_image[0][1].sub_(0.456).div_(0.224);
	//tensor_image[0][2] = tensor_image[0][2].sub_(0.406).div_(0.225);

	torch::Tensor output = model.forward({ tensor_image }).toTensor();
	//std::cout << output.sizes() << std::endl;
	//std::cout << output << std::endl;

	int emo_idx = output.argmax(1).item().toFloat();

	return emo_idx;
}

int EmoTorch::torch_process()
{
	Img_preprocessing img_tools = Img_preprocessing();
	face_detect face_detector = face_detect();
	mmod_net mmod_model;
	dlib::frontal_face_detector fog_model;

	// Create variable for Pytorch, 모델 변수 선언
	torch::jit::script::Module model;

	// Load Model, 모델 로드
	EmoTorch::torch_model_load(model);

	// Read Video
	cv::VideoCapture cap;

	// Setting camera
	img_tools.set_camera(cap);

	// Setting face_detector
	if (FACE_METHOD == 0)	// dlib
	{
		if (DLIB_STYLE == "CNN")
		{
			face_detector.load_mmod(mmod_model);
		}
		else
		{
			face_detector.load_hog(fog_model);
		}
	}

	// Start Video
	cv::Mat frame;
	cv::Mat face_img;
	int output = -1;
	std::vector<int> face_positions;

	while (true)
	{
		cap >> frame;

		double start_time = clock();
		face_detector.detect_face(mmod_model, fog_model, frame, face_positions);
		if (face_positions.size() != 0)
		{
			for (int idx = 0; idx < face_positions.size() - 3; idx += 4)
			{
				cv::Rect face_rect(cv::Point(face_positions[idx], face_positions[idx + 1]), cv::Point(face_positions[idx + 2], face_positions[idx + 3]));
				frame(face_rect).copyTo(face_img);
				cv::cvtColor(face_img, face_img, cv::COLOR_BGR2RGB);

				if (PADDING) { img_tools.add_padding(face_img); }
				img_tools.resize_img(face_img);
				output = EmoTorch::torch_predict(face_img, model);

				img_tools.draw_bbox(frame, face_positions[idx], face_positions[idx + 1], face_positions[idx + 2], face_positions[idx + 3], output);
				img_tools.put_text(frame, face_positions[idx], face_positions[idx + 1], face_positions[idx + 2], face_positions[idx + 3], output);
				
				output = -1;
			}
		}
		double terminate_time = clock();
		std::cout << "start_time : " << start_time << std::endl;
		std::cout << "terminate_time : " << terminate_time << std::endl;
		std::cout << (terminate_time - start_time) / CLOCKS_PER_SEC << std::endl;
		if (SHOW_FPS)
		{
			img_tools.put_fps(frame, (1.0 / ((terminate_time - start_time) / CLOCKS_PER_SEC)));
		}

		face_positions.clear();

		cv::imshow("test", frame);
		if (cv::waitKey(1) == 27) { break; }
	}

	return 0;
}

헤더 파일

#pragma once
#ifndef __TENSORFLOW_CLASSIFICATION_H__
#define __TENSORFLOW_CLASSIFICATION_H__

#include <opencv2/opencv.hpp>
#include <iostream>
#include <algorithm>

// 설정 파일
#include "config.h"

// 이미지 처리
#include "img_utils.h"

// dlib
#include "dlib_utils.h"

// tensorflow
#include "tensorflow/c/c_api.h"

static const int64_t face_dim[4] = { 1, IMG_SIZE, IMG_SIZE, 3 };
static std::size_t const face_ndata = IMG_SIZE * IMG_SIZE * 3 * sizeof(float);
static auto const deallocator = [](void*, std::size_t, void*) {};
static std::array<char const*, 1> tags{ "serve" };

class EmoTensorflow
{
private:
	static TF_Tensor* make_tensor_input(cv::Mat& face_crop);
	static TF_Session* tensorflow_model_load(TF_Buffer** run_options, TF_SessionOptions** session_options, TF_Graph** graph, TF_Status** status);
	static int tensorflow_predict(cv::Mat face_tensor, TF_Session** session, TF_Buffer** run_options, TF_Graph** graph, TF_Status** status);
	static void delete_variable(TF_Session** session, TF_Buffer** run_options, TF_SessionOptions** session_options, TF_Graph** graph, TF_Status** status);
public:
	static int tensorflow_process();
};

#endif

 

CPP 파일

#include "tensorflow_classification.h"

TF_Tensor* EmoTensorflow::make_tensor_input(cv::Mat& face_crop)
{
	//cv::Mat face_crop = cv::imread("./Lenna.png");
	cv::resize(face_crop, face_crop, cv::Size(IMG_SIZE, IMG_SIZE), 0, 0, cv::INTER_LINEAR);
	cv::cvtColor(face_crop, face_crop, cv::COLOR_BGR2RGB);
	face_crop.convertTo(face_crop, CV_32F, 1 / 255.0);

	return TF_NewTensor(
		TF_FLOAT, face_dim, 4, face_crop.ptr(), face_ndata, deallocator, nullptr
	);
}

TF_Session* EmoTensorflow::tensorflow_model_load(TF_Buffer** run_options, TF_SessionOptions** session_options, TF_Graph** graph, TF_Status** status)
{
	std::array<char const*, 1> tags{ "serve" };

	TF_Session* model = TF_LoadSessionFromSavedModel(
		*session_options, *run_options, "./model/mobilenetv1_64_padding",
		tags.data(), tags.size(), *graph, nullptr, *status);

	// Check Model
	if (TF_GetCode(*status) != TF_OK) {
		std::cout << TF_Message(*status) << '\n';
	}

	// Load Model
	return model;
}

int EmoTensorflow::tensorflow_predict(cv::Mat face_crop, TF_Session** session, TF_Buffer** run_options, TF_Graph** graph, TF_Status** status)
{
	TF_Tensor* face_tensor = EmoTensorflow::make_tensor_input(face_crop);

	TF_Operation* input_op = TF_GraphOperationByName(*graph, "serving_default_input_1");
	if (input_op == nullptr) {
		std::cout << "Failed to find graph operation\n" << std::endl;
	}
	TF_Operation* output_op = TF_GraphOperationByName(*graph, "StatefulPartitionedCall");
	if (output_op == nullptr) {
		std::cout << "Failed to find graph operation\n" << std::endl;
	}

	std::array<TF_Output, 1> input_ops = { TF_Output{ input_op, 0} };
	std::array<TF_Output, 1> output_ops = { TF_Output{ output_op, 0} };

	std::array<TF_Tensor*, 1> input_values{ face_tensor };
	std::array<TF_Tensor*, 7> output_values{};

	TF_SessionRun(*session,
		*run_options,
		input_ops.data(), input_values.data(), input_ops.size(),
		output_ops.data(), output_values.data(), output_ops.size(),
		nullptr, 0, nullptr, *status
	);

	auto* output_tensor = static_cast<std::array<float, 7> *>(TF_TensorData(output_values[0]));
	std::vector<std::array<float, 7>> outputs{ output_tensor, output_tensor + 1 };

	float max_value = *max_element(outputs[0].begin(), outputs[0].end());

	return (std::find(outputs[0].begin(), outputs[0].end(), max_value) - outputs[0].begin());
}

void EmoTensorflow::delete_variable(TF_Session** session, TF_Buffer** run_options, TF_SessionOptions** session_options, TF_Graph** graph, TF_Status** status)
{
	TF_DeleteBuffer(*run_options);
	TF_DeleteSessionOptions(*session_options);
	TF_DeleteSession(*session, *status);
	TF_DeleteGraph(*graph);
	TF_DeleteStatus(*status);
}

int EmoTensorflow::tensorflow_process()
{
	Img_preprocessing img_tools = Img_preprocessing();
	face_detect face_detector = face_detect();
	mmod_net mmod_model;
	dlib::frontal_face_detector fog_model;

	// Create variables for tensorflow, 모델 변수 선언
	TF_Buffer* run_options = TF_NewBufferFromString("", 0);
	TF_SessionOptions* session_options = TF_NewSessionOptions();
	TF_Graph* graph = TF_NewGraph();
	TF_Status* status = TF_NewStatus();

	// Load Model
	TF_Session* session = EmoTensorflow::tensorflow_model_load(&run_options, &session_options, &graph, &status);

	// Read Video
	cv::VideoCapture cap;

	// Setting camera
	img_tools.set_camera(cap);

	// Setting face_detector
	if (FACE_METHOD == 0)	// dlib
	{
		if (DLIB_STYLE == "CNN")
		{
			face_detector.load_mmod(mmod_model);
		}
		else
		{
			face_detector.load_hog(fog_model);
		}
	}

	// Start Video
	cv::Mat frame;
	cv::Mat face_img;
	int output = -1;
	std::vector<int> face_positions;

	while (true)
	{
		cap >> frame;

		double start_time = clock();
		face_detector.detect_face(mmod_model, fog_model, frame, face_positions);
		if (face_positions.size() != 0)
		{
			for (int idx = 0; idx < face_positions.size() - 3; idx += 4)
			{
				cv::Rect face_rect(cv::Point(face_positions[idx], face_positions[idx + 1]), cv::Point(face_positions[idx + 2], face_positions[idx + 3]));
				frame(face_rect).copyTo(face_img);
				cv::cvtColor(face_img, face_img, cv::COLOR_BGR2RGB);

				if (PADDING) { img_tools.add_padding(face_img); }
				img_tools.resize_img(face_img);
				output = EmoTensorflow::tensorflow_predict(face_img, &session, &run_options, &graph, &status);

				img_tools.draw_bbox(frame, face_positions[idx], face_positions[idx + 1], face_positions[idx + 2], face_positions[idx + 3], output);
				img_tools.put_text(frame, face_positions[idx], face_positions[idx + 1], face_positions[idx + 2], face_positions[idx + 3], output);

				output = -1;
			}
		}
		double terminate_time = clock();
		std::cout << "start_time : " << start_time << std::endl;
		std::cout << "terminate_time : " << terminate_time << std::endl;
		std::cout << (terminate_time - start_time) / CLOCKS_PER_SEC << std::endl;
		if (SHOW_FPS)
		{
			img_tools.put_fps(frame, (1.0 / ((terminate_time - start_time) / CLOCKS_PER_SEC)));
		}

		face_positions.clear();

		cv::imshow("test", frame);
		if (cv::waitKey(1) == 27) { break; }
	}

	EmoTensorflow::delete_variable(&session, &run_options, &session_options, &graph, &status);

	return 0;
}

Contorller 만들기

 

먼저 controller 폴더에 MemberController class를 만들어 줍니다.

 

그리고 public class MemberController {} 위에 @Controller 어노테이션을 걸어주면

 

자동으로 import org.springframework.stereotype.Controller; 가 자동 생성 됩니다.

 

 

@Controller 어노테이션를 걸어주면 스프링이 뜰때, Controller를 객체로 생성하고 스프링이 들고 있습니다.

 

이런걸 스프링 컨테이너에서 스프링빈이 관리된다고 표현합니다.

 

Controller에서 사용할 MemberService 객체를 선언해 줍니다.

 

생성자 생성 단축키는 Alt + insert 입니다.

 

이제 Contorller와 MemberService를 연결시켜 주겠습니다.

 

@Autowired 어노테이션을 사용해주면 Spring이 Spring Container에 있는 MemberService와 연결을 시켜줍니다.

 

 

이제는 Contriner에서 MemberService를 인식할 수 있도록

 

MemberService에 어노테이션을 걸어주도록 하겠습니다.

 

어노테이션

왼쪽부터 Controller, Repository, Service 파일 입니다.

 

그리고 MemberService에서도 MemberRepository를 필요로 하므로,

 

생성자에 @Autowired 어노테이션을 걸어주겠습니다.

 

 

스프링이 클래스의 존재를 알기 위해서는 위와 같이 각 역할에 맞는 어노테이션을 걸어줘야 인식이 가능합니다.

 

 

위와 같은 작업을 해주면 위 사진과 같이 Controller, Service, Repository가 연결되게 됩니다.

 

이러한 작업을 스프링 빈을 등록한다고 하고, 컴포넌트 스캔이라고 부릅니다.

 

@Controller, @Service, @Repository 내부를 보면 

 

@Component

@Component 어노테이션이 존재하는데, 이것이 스프링 빈에 등록해주는 역할을 합니다.

 

DI (Dependency Injection)에는 필드, setter, 생성자 주입 총 3가지 방법이 있습니다.

 

하지만 의존관계가 실행중에 동적으로 변하는 경우는 거의 없으므로 생성자 주입을 권장합니다.

 

'Web > backend' 카테고리의 다른 글

07 - 01. 회원 관리 예제  (0) 2022.10.05
06. API - 정리  (0) 2022.10.05
05. 정적 컨텐츠, MVC  (0) 2022.10.04
04. 빌드하고 실행하기  (0) 2022.10.04
03. Welcome Page 만들기  (0) 2022.09.30
std::cout << "Tensorflow Version: " << TF_Version() << std::endl;

// Create variables for tensorflow
TF_Buffer* run_options = TF_NewBufferFromString("", 0);
TF_SessionOptions* session_options = TF_NewSessionOptions();
TF_Graph* graph = TF_NewGraph();
TF_Status* status = TF_NewStatus();
std::array<char const*, 1> tags{ "serve" };

// Load Model
TF_Session* session = TF_LoadSessionFromSavedModel(
		session_options, run_options, "./model/mobilenetv1_64_padding", 
		tags.data(), tags.size(), graph, nullptr, status);
		
// Check Model
if (TF_GetCode(status) != TF_OK) {
	std::cout << TF_Message(status) << '\n';
}

TF_Operation* input_op = TF_GraphOperationByName(graph, "serving_default_input");
TF_Operation* output_op = TF_GraphOperationByName(graph, "StatefulPartitionedCall");

 

여기서 input_op와 output_op의 이름은 Saved Model CLI 에서 확인 가능합니다.

 

 

 

Windows를 사용하고 있고

 

Saved Model CLI 는 anaconda를 사용하고 있다면

 

Saved Model CLI는 아래 경로에 위치하고 있습니다.

 

 

 

 

 

 

 

C:/Users/TEST/anaconda3/envs/가상환경이름/Lib/site-packages/tensorflow/python\tools/

 

↑ Saved Model CLI 의 위치

 

이후 cmd 창에서 아래 명령어를 실행해주면

 

python saved_model_cli.py show --dir ./mobilenetv1_64_padding --tag_set serve --signature_def serving_default

 

아래와 같은 결과가 나오게 됩니다.

 

 

모델마다 input의 개수가 달라질 수 있으니 유의해야 합니다.

 

 

 

 

 

 

 

출처 :

https://int-i.github.io/cpp/2020-11-18/libtensorflow/

https://www.tensorflow.org/guide/saved_model#details_of_the_savedmodel_command_line_interface

https://sequencedata.tistory.com/15

https://www.dbersan.com/blog/tensorflow-cpp1/

https://github.com/tensorflow/models/issues/6150

Python 환경에서 만든 모델을 libtorch에 적용하기 위해서는 다른 방식으로 .pt를 만들어 주어야 합니다.

 

총 두가지 방법이 있고 Pytorch 공식문서를 참고했습니다.

 

# torch.jit.trace 사용
model.eval()
example = torch.rand(1, 3, 64, 64)
trace_module = torch.jit.trace(model, example)
trace_module.save("./trace_module.pt")

# torch.jit.script 사용
script_module = torch.jit.script(model)
script_module.save("./script_module.pt")

 

위와 같은 방법으로 .pt 파일은 생성해주어야 libtorch에 적용 가능합니다.

 

일반적으로 .save 를 통해서 저장할 경우, 에러가 발생하게 됩니다.

+ Recent posts