MongoDB.local SF, Jan 15: See the speaker lineup & ship your AI vision faster. Use WEB50 to save 50%
Find out more >
Docs Menu
Docs Home
/ /

튜토리얼: AWS 기반과 벡터 검색 통합

이 튜토리얼에서는 고 (Go), MongoDB Vector Search 및 AWS 베드락 AI 모델을 사용하여 얼굴을 일치시키는 AI 기반 백엔드 빌드 방법을 보여줍니다. 이 애플리케이션 업로드된 이미지를 처리하고, 벡터 임베딩을 생성하고, MongoDB Atlas 에서 유사한 얼굴을 검색하고, 설명과 함께 가장 가까운 일치 항목 세 개를 반환합니다.

백엔드 다음 작업을 수행합니다.

  • 이미지를 800x600 JPEG 형식으로 표준화합니다.

  • AWS 기반을 사용하여 1024차원 임베딩을 생성합니다.

  • MongoDB Vector Search를 사용하여 MongoDB Atlas 쿼리하기

  • Claude를 사용하여 유사성 설명 생성

임베딩은 이미지 특성을 인코딩하는 부동 점 숫자로 구성된 벡터입니다. 유사한 이미지는 유사한 벡터를 생성하므로 n차원 공간에서 벡터 근접성을 비교하여 일치하는 항목을 찾을 수 있습니다. 1024범위 임베딩에는 (-1.0, 1.0).

MongoDB Atlas 참고 이미지를 위해 미리 계산된 임베딩을 저장합니다. 사용자가 이미지를 업로드하면 백엔드 임베딩을 생성하고 MongoDB 벡터 검색을 사용하여 가장 일치하는 이미지를 찾습니다.

애플리케이션 요청을 프로세스 위해 다음 단계를 수행합니다.

  1. 프론트엔드 이미지를 캡처하여 기본64인코딩된 JSON 요청 으로 백엔드 에 전송합니다.

  2. 백엔드 이미지를 800x600 JPEG 형식으로 표준화합니다.

  3. AWS 기반의 amazon.titan-embed-image-v1 모델은 이미지에서 임베딩을 생성합니다.

  4. MongoDB Vector Search는 가장 근접하게 일치하는 세 개의 임베딩을 찾습니다.

  5. AWS 기반암의 anthropic.claude-3-sonnet-20240229-v1:0 모델은 유사성에 대한 언어 설명을 생성합니다.

  6. 백엔드 일치하는 이미지와 설명을 프론트엔드 에 반환합니다.

mongodb-celeb-search.com.애플리케이션 테스트할 수 있습니다.

이 튜토리얼에서는 다음 조치를 수행하는 방법을 보여줍니다.

  • 이미지 처리 요청을 처리하다 HTTP 서버 만들기

  • 업로드된 이미지를 일관적인 형식으로 표준화

  • AWS 기반을 사용하여 벡터 임베딩 생성

  • MongoDB Atlas 쿼리하여 유사한 이미지 찾기

  • 이미지 유사성에 대한 언어 설명 생성

1

이 튜토리얼을 시작하기 전에 다음 구성 요소가 있는지 확인하세요.

  • 클러스터 가 구성된 MongoDB Atlas 계정. Atlas cluster 설정하다 방법을 학습하려면 MongoDB 시작하기 가이드를 참조하세요.

  • 베드락 액세스 있는 활성 AWS 계정.

  • ~/.aws/config~/.aws/credentials에 구성된 AWS 자격 증명 . AWS 자격 증명 설정에 대한 자세한 학습 은 AWS 자격 증명 파일 설명서를 참조하세요.

  • Go가 설치되었습니다.

2

프로젝트 에 대한 새 디렉토리 만들고 해당 디렉토리로 이동합니다.

모듈을 초기화하려면 프로젝트 디렉토리 에서 다음 명령을 실행 .

go mod init github.com/jdortiz/goai

그런 다음 기본 애플리케이션 구조와 동일한 디렉토리 에 server.go 파일 만듭니다.

package main
import (
"context"
"log"
"net/http"
"os"
"github.com/joho/godotenv"
)
type App struct {
}
func (app App) Start() error {
const serverAddr string = "0.0.0.0:3001"
log.Printf("Starting HTTP server: %s\n", serverAddr)
return http.ListenAndServe(serverAddr, nil)
}
func main() {
app := App{}
log.Println(app.Start())
}
3

App 유형에 핸들러 메서드를 추가하고 라우터에 등록하려면 server.go 파일 에 imageSearch() 메서드를 추가하고 Start() 메서드에 등록합니다.

func (app App) imageSearch(w http.ResponseWriter, r *http.Request) {
log.Println("Image search invoked")
}
func (app App) Start() error {
const serverAddr string = "0.0.0.0:3001"
http.HandleFunc("POST /api/search", app.imageSearch)
log.Printf("Starting HTTP server: %s\n", serverAddr)
return http.ListenAndServe(serverAddr, nil)
}

프로젝트 디렉토리 에서 다음 명령을 실행 하여 애플리케이션 시작합니다.

go mod tidy
go run server.go

엔드포인트를 테스트하려면 명령줄 에서 다음 명령을 실행 .

curl -IX POST localhost:3001/api/search
4

요청 구조를 정의하고 이미지 표준화 로직을 추가하려면 server.go 파일 에 다음 코드를 추가합니다.

type CelebMatchRequest struct {
Image64 string `json:"img"`
}
// Receives a base64 encoded image
func standardizeImage(imageB64 string) (*string, error) {
// Get the base64 decoder as an io.Reader and use it to decode the image from the data
b64Decoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(imageB64))
origImg, _, err := image.Decode(b64Decoder)
if err != nil {
return nil, fmt.Errorf("standardizing image failed: %w", err)
}
// Resize to 800x600
resizedImg := image.NewRGBA(image.Rect(0, 0, 800, 600))
draw.NearestNeighbor.Scale(resizedImg, resizedImg.Rect, origImg, origImg.Bounds(), draw.Over, nil)
// Reencode the image to JPEG format with Q=85
var jpegToSend bytes.Buffer
if err = jpeg.Encode(&jpegToSend, resizedImg, &jpeg.Options{Quality: 85}); err != nil {
return nil, fmt.Errorf("standardizing image failed: %w", err)
}
// Re-encode to base64
stdImgB64 := base64.StdEncoding.EncodeToString(jpegToSend.Bytes())
return &stdImgB64, nil
}

이미지를 디코딩하고 표준화하도록 핸들러를 업데이트 하려면 다음 코드에 표시된 대로 imageSearch() 메서드를 수정합니다.

func (app App) imageSearch(w http.ResponseWriter, r *http.Request) {
// Deserialize request
var imgReq CelebMatchRequest
err := json.NewDecoder(r.Body).Decode(&imgReq)
if err != nil {
log.Println("ERR: parsing json data", err)
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// Split image into metadata and data
imgParts := strings.Split(imgReq.Image64, ",")
parts := len(imgParts)
if parts != 2 {
log.Printf("ERR: expecting metadata and data. Got %d parts\n", parts)
http.Error(w, fmt.Sprintf("expecting metadata and data. Got %d parts", parts), http.StatusBadRequest)
return
}
// Decode image from base 64, resize image to 800x600 with Q=85, and re-encode to base64
stdImage, err := standardizeImage(imgParts[1])
if err != nil {
log.Println("ERR:", err)
http.Error(w, "Error standardizing image", http.StatusInternalServerError)
return
}
}

프로젝트 디렉토리 에서 다음 명령을 실행 하여 이미지 편집 모듈을 설치합니다.

go get golang.org/x/image/draw
5

AWS SDK를 설치하고 App 구조체에 구성 정보를 추가하려면 프로젝트 디렉토리 에서 다음 명령을 실행 .

go get github.com/aws/aws-sdk-go-v2/config
go get github.com/aws/aws-sdk-go-v2/service/bedrockruntime

그런 다음 server.go 파일 의 App 구조체에 다음 AWS 구성 필드를 추가합니다.

type App struct {
config *aws.Config
bedrock *bedrockruntime.Client
}

AWS 구성을 초기화하려면 server.go 파일 에 다음 메서드를 추가합니다.

func connectToAWS(ctx context.Context) (*aws.Config, error) {
const dfltRegion string = "us-east-1"
const credAccount string = "your-account-name"
cfg, err := config.LoadDefaultConfig(ctx,
config.WithSharedConfigProfile(credAccount),
config.WithRegion(dfltRegion),
)
return &cfg, err
}
func NewApp(ctx context.Context) (*App, error) {
cfg, err := connectToAWS(ctx)
if err != nil {
log.Println("ERR: Couldn't load default configuration. Have you set up your AWS account?", err)
return nil, err
}
bedrockClient := bedrockruntime.NewFromConfig(*cfg)
return &App{
config: cfg,
bedrock: bedrockClient,
}, nil
}

마지막으로 다음 코드를 추가하여 생성자를 사용하도록 main() 메서드를 업데이트 .

func main() {
ctx := context.Background()
app, err := NewApp(ctx)
if err != nil {
panic(err)
}
log.Println(app.Start())
}
6

요청 구조를 정의하고 임베딩을 계산하는 메서드를 만들려면 server.go 파일 에 다음 코드를 추가하세요.

const titanEmbedImgV1ModelId string = "amazon.titan-embed-image-v1"
const contentTypeJson = "application/json"
type EmbeddingConfig struct {
OutputEmbeddingLength int `json:"outputEmbeddingLength"`
}
type BedrockRequest struct {
InputImage string `json:"inputImage"`
EmbeddingConfig EmbeddingConfig `json:"embeddingConfig"`
InputText *string `json:"inputText,omitempty"`
}
func (app App) computeImageEmbedding(ctx context.Context, image string) ([]float64, error) {
payload := BedrockRequest{
InputImage: image,
EmbeddingConfig: EmbeddingConfig{
OutputEmbeddingLength: 1024,
},
InputText: nil,
}
bedrockBody, err := json.Marshal(payload)
if err != nil {
return nil, fmt.Errorf("failed to get embedding from bedrock: %w", err)
}
bedrockReq := bedrockruntime.InvokeModelInput{
ModelId: aws.String(titanEmbedImgV1ModelId),
Body: bedrockBody,
ContentType: aws.String(contentTypeJson),
}
embeddingResp, err := app.bedrock.InvokeModel(ctx, &bedrockReq)
if err != nil {
return nil, fmt.Errorf("failed to get embedding from bedrock: %w", err)
}
result := gjson.GetBytes(embeddingResp.Body, "embedding")
var embedding []float64
result.ForEach(func(key, value gjson.Result) bool {
embedding = append(embedding, value.Float())
return true
})
return embedding, nil
}

그런 다음 프로젝트 디렉토리 에서 다음 명령을 실행 하여 GJSON을 설치합니다.

go get github.com/tidwall/gjson

임베딩을 계산하도록 핸들러를 업데이트 하려면 다음 코드에 표시된 대로 imageSearch 메서드를 수정합니다.

func (app App) imageSearch(w http.ResponseWriter, r *http.Request) {
// ...existing code...
// Compute the embedding using titan-embed-image-v1
embedding, err := app.computeImageEmbedding(r.Context(), *stdImage)
if err != nil {
log.Println("ERR:", err)
http.Error(w, "Error computing embedding", http.StatusInternalServerError)
return
}
}
7

프로젝트 디렉토리 에서 .env 파일 만듭니다. 이 파일 에 다음 코드를 추가하여 MongoDB Atlas 연결 URI를 저장 . 자리 표시자를 실제 클러스터 및 자격 증명 정보로 바꿉니다. 연결 URI를 조회 방법을 학습 MongoDB 시작하기 가이드 참조하세요.

MONGODB_URI=mongodb+srv://<username>:<password>@<cluster>.mongodb.net/

그런 다음 프로젝트 디렉토리 에서 다음 명령을 실행 하여 필요한 모듈을 설치합니다.

go get github.com/joho/godotenv
go get go.mongodb.org/mongo-driver/v2

App 구조체에 MongoDB 클라이언트 필드 추가합니다.

type App struct {
client *mongo.Client
config *aws.Config
bedrock *bedrockruntime.Client
}

MongoDB 클라이언트 초기화하는 메서드를 만들려면 server.go 파일 에 다음 코드를 추가합니다.

func newDBClient(uri string) (*mongo.Client, error) {
serverAPI := options.ServerAPI(options.ServerAPIVersion1)
opts := options.Client().ApplyURI(uri).SetServerAPIOptions(serverAPI)
client, err := mongo.Connect(opts)
if err != nil {
return nil, err
}
return client, nil
}
func (app *App) Close() {
if err := app.client.Disconnect(context.Background()); err != nil {
panic(err)
}
}

생성자 서명 및 구현 업데이트 하려면 다음 코드에 표시된 대로 NewApp() 메서드를 수정합니다.

func NewApp(ctx context.Context, uri string) (*App, error) {
cfg, err := connectToAWS(ctx)
if err != nil {
log.Println("ERR: Couldn't load default configuration. Have you set up your AWS account?", err)
return nil, err
}
bedrockClient := bedrockruntime.NewFromConfig(*cfg)
client, err := newDBClient(uri)
if err != nil {
log.Println("ERR: connecting to MongoDB cluster:", err)
return nil, err
}
return &App{
client: client,
config: cfg,
bedrock: bedrockClient,
}, nil
}

마지막으로 환경 변수를 로드하려면 다음 코드에 표시된 대로 main() 메서드를 업데이트 .

func main() {
var uri string
err := godotenv.Load()
if err != nil {
log.Fatal("Unable to load .env file")
}
if uri = os.Getenv("MONGODB_URI"); uri == "" {
log.Fatal("You must set your 'MONGODB_URI' environment variable. See\n\t https://mongodb.com/ko-kr/docs/drivers/go/current/usage-examples/")
}
ctx := context.Background()
app, err := NewApp(ctx, uri)
if err != nil {
panic(err)
}
defer func() {
app.Close()
}()
log.Println(app.Start())
}
8

MongoDB 에서 유사한 이미지를 찾는 메서드를 만들려면 server.go 파일 에 다음 코드를 추가합니다.

func (app App) findSimilarImages(ctx context.Context, embedding []float64) ([]string, error) {
imgCollection := app.client.Database("celebrity_matcher").Collection("celeb_images")
vectorSchStage := bson.D{{"$vectorSearch", bson.D{{"index", "vector_index"},
{"path", "embeddings"},
{"queryVector", embedding},
{"numCandidates", 15},
{"limit", 3}}}}
projectStage := bson.D{{"$project", bson.D{{"image", 1}}}}
pipeline := mongo.Pipeline{vectorSchStage, projectStage}
imgCursor, err := imgCollection.Aggregate(ctx, pipeline)
if err != nil {
return nil, fmt.Errorf("failed to get similar images from the database: %w", err)
}
similarImgs := []struct {
Id bson.ObjectID `bson:"_id,omitempty"`
Image string `bson:"image"`
}{}
if err = imgCursor.All(ctx, &similarImgs); err != nil {
return nil, fmt.Errorf("failed to get similar images from the database: %w", err)
}
var images []string
var stdImage *string
for _, item := range similarImgs {
stdImage, err = standardizeImage(item.Image)
if err != nil {
return nil, fmt.Errorf("failed to standardize similar images: %w", err)
}
images = append(images, *stdImage)
}
return images, nil
}

유사한 이미지를 찾도록 핸들러를 업데이트 하려면 다음과 같이 imageSearch 메서드를 수정합니다.

func (app App) imageSearch(w http.ResponseWriter, r *http.Request) {
// ...existing code...
// Find similar images using vector search in MongoDB
images, err := app.findSimilarImages(r.Context(), embedding)
if err != nil {
log.Println("ERR:", err)
http.Error(w, "Error getting similar images", http.StatusInternalServerError)
return
}
}
9

Claude 요청 구조를 정의하고 설명을 생성하는 메서드를 만들려면 server.go 파일 에 다음 코드를 추가하세요.

const claude3SonnetV1ModelId string = "anthropic.claude-3-sonnet-20240229-v1:0"
type ClaudeBodyMsgSource struct {
Type string `json:"type"`
MediaType *string `json:"media_type,omitempty"`
Data *string `json:"data,omitempty"`
}
type ClaudeBodyMsgContent struct {
Type string `json:"type"`
Source *ClaudeBodyMsgSource `json:"source,omitempty"`
Text *string `json:"text,omitempty"`
}
type ClaudeBodyMsg struct {
Role string `json:"role"`
Content []ClaudeBodyMsgContent `json:"content"`
}
type ClaudeBody struct {
AnthropicVersion string `json:"anthropic_version"`
MaxTokens int `json:"max_tokens"`
System string `json:"system"`
Messages []ClaudeBodyMsg `json:"messages"`
}
func (app App) getImageSimilaritiesDescription(ctx context.Context, imgB64 string, similarImgB64 []string) (*string, error) {
const mediaTypeImage = "image/jpeg"
prompt := "Please let the user know how their first image is similar to the other 3 and which one is the most similar?"
payload := ClaudeBody{
AnthropicVersion: "bedrock-2023-05-31",
MaxTokens: 1000,
System: "Please act as face comparison analyzer.",
Messages: []ClaudeBodyMsg{
{
Role: "user",
Content: []ClaudeBodyMsgContent{
{
Type: "image",
Source: &ClaudeBodyMsgSource{
Type: "base64",
MediaType: aws.String(mediaTypeImage),
Data: &imgB64,
},
},
{
Type: "image",
Source: &ClaudeBodyMsgSource{
Type: "base64",
MediaType: aws.String(mediaTypeImage),
Data: &similarImgB64[0],
},
},
{
Type: "image",
Source: &ClaudeBodyMsgSource{
Type: "base64",
MediaType: aws.String(mediaTypeImage),
Data: &similarImgB64[1],
},
},
{
Type: "image",
Source: &ClaudeBodyMsgSource{
Type: "base64",
MediaType: aws.String(mediaTypeImage),
Data: &similarImgB64[2],
},
},
{
Type: "text",
Text: &prompt,
},
},
},
},
}
bedrockBody, err := json.Marshal(payload)
if err != nil {
return nil, fmt.Errorf("failed to get embedding from bedrock: %w", err)
}
bedrockReq := bedrockruntime.InvokeModelInput{
ModelId: aws.String(claude3SonnetV1ModelId),
Body: bedrockBody,
ContentType: aws.String(contentTypeJson),
Accept: aws.String(contentTypeJson),
}
bedrockResp, err := app.bedrock.InvokeModel(ctx, &bedrockReq)
if err != nil {
return nil, fmt.Errorf("failed to get embedding from bedrock: %w", err)
}
description := gjson.GetBytes(bedrockResp.Body, "content.0.text").String()
return &description, nil
}

설명을 생성하도록 핸들러를 업데이트 하려면 다음 코드에 표시된 대로 imageSearch 메서드를 수정합니다.

func (app App) imageSearch(w http.ResponseWriter, r *http.Request) {
// ...existing code...
description, err := app.getImageSimilaritiesDescription(r.Context(), *stdImage, images)
if err != nil {
log.Println("ERR: failed to describe similarities with images", err)
http.Error(w, "Error describing similarities with images", http.StatusInternalServerError)
return
}
}
10

응답 구조를 정의하고 핸들러를 완료하려면 server.go 파일 에 다음 코드를 추가합니다.

type CelebMatchResponse struct {
Description string `json:"description"`
Images []string `json:"images"`
}
func (app App) imageSearch(w http.ResponseWriter, r *http.Request) {
// ...existing code...
response := CelebMatchResponse{
Description: *description,
Images: images,
}
jData, err := json.Marshal(response)
if err != nil {
log.Println("error serializing json", err)
http.Error(w, "error serializing json", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", contentTypeJson)
w.Header().Set("Content-Length", strconv.Itoa(len(jData)))
w.WriteHeader(http.StatusOK)
w.Write(jData)
}

프로젝트 디렉토리 에서 다음 명령을 실행 하여 애플리케이션 한 번 더 시작합니다.

go mod tidy
go run server.go

웹 브라우저에서 http://localhost:3001 로 이동하여 기본64인코딩된 이미지를 사용하여 /api/search 에 POST 요청 전송하여 API 테스트합니다.

MongoDB Vector Search에 대해 자세히 학습하려면 MongoDB Server 매뉴얼의 MongoDB Vector Search 문서를 참조하세요.

AWS 기반에 대해 자세히 학습하려면 AWS 기반 설명서를 참조하세요.

돌아가기

TLS 보안 프로토콜