G-gen ã®çŠäºã§ããåœèšäºã§ã¯ãGoogle ãæäŸãããã«ãã¢ãŒãã«çæ AI ã¢ãã« Gemini ãšãç»åçæ AI ã¢ãã« Imagen ã䜿çšããŠãã¢ããããŒãç»åããé¡äŒŒç»åãçæãã Web ã¢ããªãéçºããæé ãã玹ä»ããŸãã ã¯ããã« åœèšäºã®æŠèŠ å®è¡ã€ã¡ãŒãž å©çšãµãŒãã¹ã»ã©ã€ãã©ãª ãœãŒã¹ã³ãŒã Python ã®ããŒãžã§ã³ requirements.txt main.py ããŒã«ã«ã§ã®åäœç¢ºèª ããŒã«ã«å®è¡ ããŒã«ã«ã§èµ·åããã¢ããªãžæ¥ç¶ Google Cloud ãžã®ããã〠Cloud Run ã®äœ¿çš ãã£ã¬ã¯ããªæ§æ Dockerfile ã®äœæ Cloud Run ã«ããã〠åäœç¢ºèª Cloud Run ã®ã¢ã¯ã»ã¹å
å¶åŸ¡ã«ã€ã㊠ã¯ããã« åœèšäºã®æŠèŠ åœèšäºã§ã¯ãGoogle ãæäŸãããã«ãã¢ãŒãã«çæ AI ã¢ãã« Gemini ãšãç»åçæ AI ã¢ãã« Imagen ã䜿çšããŠãã¢ããããŒãããç»åããé¡äŒŒããç»åãçæãã Web ã¢ããªãéçºããŠã¿ãŸãã ãã®ã¢ããªã§ã¯ã以äžã®æ©èœãæäŸããŸãã ç»åã®ç¹åŸŽãæœåºããããã®ããã¹ãããã³ãããå
¥åããã€ã³ã¿ãŒãã§ã€ã¹ Gemini 1.5 Pro ã䜿çšããŠãã¢ããããŒãããç»åã®ç¹åŸŽãæœåº æœåºããç»åã®ç¹åŸŽæ
å ±ãç·šé Imagen 2 ã䜿çšããŠãæœåºããç»åã®ç¹åŸŽæ
å ±ããé¡äŒŒç»åãçæ å®è¡ã€ã¡ãŒãž Web ã€ã³ã¿ãŒãã§ã€ã¹ã§ãããã³ãããããã¹ãã§å
¥åããç»åãã¢ããããŒãããŸãããã®åŸãã¢ããããŒãç»åã®ç¹åŸŽæœåºããã¿ã³ãæŒäžãããšãã¢ããããŒãããç»åã®ç¹åŸŽã衚瀺ãããŸãã ç»åã®ç¹åŸŽãæœåº ãã®åŸããã«ãç»åã®ç¹åŸŽããé¡äŒŒç»åãçæããã¿ã³ãæŒäžãããšãç»åã®ç¹åŸŽæ
å ±ãããšã«ããé¡äŒŒç»åãçæãããŸãã é¡äŒŒç»åãçæ å©çšãµãŒãã¹ã»ã©ã€ãã©ãª åœèšäºã§ã¯ã以äžã®èŠçŽ ã䜿ã£ãŠã¢ããªãéçºããŸããã Gemini Pro Google ãæäŸããçæ AI ã¢ãã«ã§ãããããã¹ããç»åãåç»ãªã©ã®è€æ°ã®çš®é¡ã®ããŒã¿ãæ±ãããšãã§ãããã«ãã¢ãŒãã«ãªçæ AI ã¢ãã«ã§ãã åè : Gemini Proã䜿ã£ãŠã¿ããGoogleã®ææ°çæAIã¢ãã« Imagen Google ãæäŸããç»åçæ AI ã¢ãã«ã§ãã 2024幎7æçŸåšãImagen ã䜿çšããããã«ã¯ç³è«ãå¿
èŠãšãªããŸãã åè : Imagenã䜿ã£ãã·ã³ãã«ãªç»åçæAIã¢ããªãéçºããŠã¿ã Gradio æ©æ¢°åŠç¿ Web ã¢ããªã容æã«æ§ç¯ã§ãã Python ãã¬ãŒã ã¯ãŒã¯ã§ãã åè : Gradio Docs - Blocks Cloud Run Google Cloud ã®ãã³ã³ãããå®è¡ã®ããã®ãã«ãããŒãžããµãŒãã¹ åè : Cloud Run ã培åºè§£èª¬ïŒ ãœãŒã¹ã³ãŒã Python ã®ããŒãžã§ã³ åœèšäºã§ã¯ãPython 3.12.4 ã䜿ã£ãŠéçºããŠããŸãã $ python --version Python 3 . 12 . 4 requirements.txt 䜿çšããã©ã€ãã©ãªãã以äžã®ãšãã requirements.txt ã«å®çŸ©ããŸãã google-cloud-aiplatform==1.56.0 google-generativeai==0.5.4 gradio==4.37.2 main.py éçºããã³ãŒãã®å
šæã以äžã«èšèŒããŸãã import os import sys import traceback import gradio as gr import gradio.blocks as blocks import vertexai from vertexai.preview.generative_models import GenerationConfig, GenerativeModel, Part from vertexai.preview.vision_models import ImageGenerationModel SUPPORTED_IMAGE_EXTENSIONS = { "png" , "jpeg" , "jpg" , } MAX_PROMPT_SIZE_MB = 4.0 multimodal_model = None # åæååŠç def initialize_variables () -> None : global multimodal_model try : project_id = os.environ.get( "PROJECT_ID" ) if not project_id: raise ValueError ( "ç°å¢å€æ°ã«ãPROJECT_IDããèšå®ãããŠããŸãã" ) location = os.environ.get( "LOCATION" ) if not location: raise ValueError ( "ç°å¢å€æ°ã«ãLOCATIONããèšå®ãããŠããŸãã" ) # Vertex AI ã€ã³ã¹ã¿ã³ã¹ã®åæå vertexai.init(project=project_id, location=location) # Gemini ã¢ãã«ã®åæå multimodal_model = GenerativeModel( "gemini-1.5-pro-001" ) except Exception : print (traceback.format_exc()) raise # ãã¡ã€ã«ã®æ¡åŒµåãååŸ def get_file_extension (file_path: str ) -> str : _, extension = os.path.splitext(file_path) return extension[ 1 :] # extension ããµããŒããããŠãããå€å® def is_supported_image_extensions (extension: str ) -> bool : return extension in SUPPORTED_IMAGE_EXTENSIONS # mime_type ãååŸ def get_mime_type (extension: str ) -> str : # ãµããŒããããŠããªãæ¡åŒµåã®å Žå if not is_supported_image_extensions(extension): raise ValueError (f 'ãµããŒãããŠãã圢åŒã¯ {", ".join(SUPPORTED_IMAGE_EXTENSIONS)} ã§ãã' ) return "image/jpeg" if extension in [ "jpg" , "jpeg" ] else f "image/{extension}" # ããã³ãããµã€ãºã®èšç® def calculate_prompt_size_mb (text: str , file_path: str ) -> float : # ããã¹ããµã€ãºããã€ãåäœã§ååŸ text_size_bytes = sys.getsizeof(text) # ãã¡ã€ã«ãµã€ãºããã€ãåäœã§ååŸ file_size_bytes = os.path.getsize(file_path) # ãã€ãããã¡ã¬ãã€ãã«åäœå€æ prompt_size_mb = (text_size_bytes + file_size_bytes) / 1048576 return prompt_size_mb # Gemini ããã®åºåãååŸ def extraction_image_feature ( prompt: str , file_path: str , temperature: float , max_output_tokens: int , top_k: int , top_p: float , ) -> str : try : # ããã¹ããšç»åã®äž¡æ¹ãå
¥åãããŠããªãå Žå if not prompt or not file_path: raise ValueError ( "ããã¹ããšç»åãå
¥åããŠäžããã" ) # ããã³ãããµã€ãºãååŸ prompt_size_mb = calculate_prompt_size_mb(text=prompt, file_path=file_path) # ããã³ãããµã€ãºãäžéãè¶
ããæ if prompt_size_mb > MAX_PROMPT_SIZE_MB: raise ValueError ( "ç»åãšããã¹ããå«ãããã³ãããµã€ãºã¯{MAX_PROMPT_SIZE_MB}MBæªæºãšããŠäžããã \n çŸåšã¯{round(prompt_size_mb, 1)}MBã§ãã" ) # ãã¡ã€ã«ã®æ¡åŒµåãååŸ extension = get_file_extension(file_path) # ãµããŒããããŠããªãæ¡åŒµåã®å Žå if not is_supported_image_extensions(extension): raise ValueError (f 'ãµããŒãããŠãã圢åŒã¯ {", ".join(SUPPORTED_IMAGE_EXTENSIONS)} ã§ãã' ) # Gemini ã«æž¡ãç»åæ
å ±ãçæ with open (file_path, "rb" ) as f: image_content = Part.from_data(data=f.read(), mime_type=get_mime_type(extension)) # Gemini ã«ãªã¯ãšã¹ããéä¿¡ response = multimodal_model.generate_content( contents=[image_content, prompt], generation_config=GenerationConfig( temperature=temperature, top_p=top_p, top_k=top_k, max_output_tokens=max_output_tokens ), ) return response.text except ValueError as e: raise gr.Error( str (e)) except Exception : print (traceback.format_exc()) raise gr.Error( "äºæãã¬ãšã©ãŒãçºçããŸãã" ) # é¡äŒŒç»åãçæ def generate_similar_image ( model_name: str , prompt: str , negative_prompt: str , guidance_scale: float , aspect_ratio: str , number_of_images: int , seed: int , ): PROMPT_PREFIX = """ ç»åã®ç¹åŸŽãæœåºããæç« ã以äžã«èšèŒããŸãã èšèŒãããç¹åŸŽãå
ã«é¡äŒŒããç»åãçæããŠãã ããã """ try : if not prompt: raise ValueError ( "ç»åã®ç¹åŸŽãå
¥åããŠäžããã" ) prompt = PROMPT_PREFIX + prompt if not negative_prompt: negative_prompt = None if seed < 0 or seed > 2147483647 : seed = None model = ImageGenerationModel.from_pretrained(model_name) generate_response = model.generate_images( prompt=prompt, negative_prompt=negative_prompt, number_of_images=number_of_images, guidance_scale= float (guidance_scale), aspect_ratio=aspect_ratio, language= "ja" , seed=seed, ) images = [] for index, result in enumerate (generate_response): images.append(generate_response[index]._pil_image) return images except ValueError as e: raise gr.Error( str (e)) except Exception : print (traceback.format_exc()) raise gr.Error( "äºæãã¬ãšã©ãŒãçºçããŸãã" ) # ã¡ã€ã³ã³ã³ãã³ãã® Gradio ãœãŒã¹ãçæ def generate_main_content (base_app: blocks) -> None : with base_app: with gr.Row(): gr.Markdown( """ # 1. ã¢ããããŒãããç»åã®ç¹åŸŽãæœåº """ ) with gr.Row(): with gr.Column(): txt_extraction_image_feature_in_prompt = gr.Textbox( placeholder= "ããã¹ããå
¥åããŠäžããã" , value= "äœã衚ããç»åã§ããããšãç©ã®ç¹åŸŽïŒãžã£ã³ã«ãè²ã圢ç¶ãªã©ïŒãç®æ¡æžãã§æããŠãã ãã" , container= True , label= "ã¢ããããŒãç»åã®ç¹åŸŽãæœåºããããã³ããïŒä¿®æ£å¯ïŒ" , scale= 1 , ) img_extraction_image_feature_in_image = gr.Image( type = "filepath" , sources=[ "upload" ], scale= 1 ) with gr.Column(): txt_extraction_image_feature_out = gr.Textbox(scale= 2 , label= "ç»åã®ç¹åŸŽïŒä¿®æ£å¯ïŒ" ) with gr.Row(): with gr.Column(): with gr.Accordion( "ãã©ã¡ãŒã¿ãã¥ãŒãã³ã°é
ç®" , open = False ): with gr.Row(): sld_extraction_image_feature_in_temperature = gr.Slider( label= "Temperature" , minimum= 0 , maximum= 1 , step= 0.1 , value= 0.4 , interactive= True ) sld_extraction_image_feature_in_max_output_tokens = gr.Slider( label= "Max Output Token" , minimum= 1 , maximum= 2048 , step= 1 , value= 1024 , interactive= True ) sld_extraction_image_feature_in_top_k = gr.Slider( label= "Top-K" , minimum= 1 , maximum= 40 , step= 1 , value= 32 , interactive= True ) sld_extraction_image_feature_in_top_p = gr.Slider( label= "Top-P" , minimum= 0.1 , maximum= 1 , step= 0.1 , value= 1 , interactive= True ) with gr.Row(): btn_refresh = gr.Button(value= "ç»é¢å
šäœãã¯ãªã¢" ) btn_extraction_image_feature = gr.Button(value= "ã¢ããããŒãç»åã®ç¹åŸŽæœåº" ) with gr.Row(): gr.Markdown( """ ã # 2. æœåºããç»åã®ç¹åŸŽãããšã«é¡äŒŒã®ç»åãçæ """ ) with gr.Row(): glr_generate_similar_image_out = gr.Gallery( label= "Generated Images" , show_label= True , elem_id= "gallery" , columns=[ 2 ], object_fit= "contain" , height= "auto" , ) with gr.Row(): with gr.Accordion( "ãã©ã¡ãŒã¿ãã¥ãŒãã³ã°é
ç®" , open = False ): drp_generate_similar_image_in_model = gr.Dropdown( label= "䜿çšããã¢ãã«" , choices=[ "imagegeneration@002" , "imagegeneration@006" ], value= "imagegeneration@006" , ) txt_generate_similar_image_in_negative_prompt = gr.Textbox( label= "ãã¬ãã£ãããã³ãã" , placeholder= "衚瀺ããããªãå
容ãå®çŸ©ããŸã" , value= "" , ) drp_generate_similar_image_in_guidance_scale = gr.Dropdown( label= "åºåã€ã¡ãŒãžãµã€ãº" , choices=[ 256.0 , 1024.0 , 1536.0 ], value= "1536" , ) drp_generate_similar_image_in_aspect_ratio = gr.Dropdown( label= "ã¢ã¹ãã¯ãæ¯" , choices=[ "1:1" , "9:16" , "16:9" , "3:4" , "4:3" ], value= "1:1" , ) num_generate_similar_image_in_number_of_images = gr.Number( label= "衚瀺件æ°" , info= "çæãããç»åã®æ°ãæå®ã§ããæŽæ°å€: 1ïœ4ãããã©ã«ãå€: 4" , value= 4 , ) num_generate_similar_image_in_seed = gr.Number( label= "seed" , info= "å¿
èŠã«å¿ããŠçµæãåçŸã§ããããã«ãå¯èœã§ããã°ã·ãŒãã䜿çšããŠãã ãããæŽæ°ç¯å²: (0, 2147483647)" , value=- 1 , ) with gr.Row(): btn_refresh2 = gr.Button(value= "ç»é¢å
šäœãã¯ãªã¢" ) btn_generate_similar_image = gr.Button(value= "ç»åã®ç¹åŸŽããé¡äŒŒç»åãçæ" ) # Submitãã¿ã³ãæŒäžããããšãã®åŠç btn_extraction_image_feature.click( extraction_image_feature, [ txt_extraction_image_feature_in_prompt, img_extraction_image_feature_in_image, sld_extraction_image_feature_in_temperature, sld_extraction_image_feature_in_max_output_tokens, sld_extraction_image_feature_in_top_k, sld_extraction_image_feature_in_top_p, ], txt_extraction_image_feature_out, ) btn_generate_similar_image.click( fn=generate_similar_image, inputs=[ drp_generate_similar_image_in_model, txt_extraction_image_feature_out, txt_generate_similar_image_in_negative_prompt, drp_generate_similar_image_in_guidance_scale, drp_generate_similar_image_in_aspect_ratio, num_generate_similar_image_in_number_of_images, num_generate_similar_image_in_seed, ], outputs=glr_generate_similar_image_out, ) # Refreshãã¿ã³ãæŒäžããããšãã®åŠç btn_refresh.click( None , js= "window.location.reload()" ) btn_refresh2.click( None , js= "window.location.reload()" ) app = gr.Blocks() try : initialize_variables() generate_main_content(app) except Exception as e: error_mesasge = "" if isinstance (e, ValueError ): error_mesasge = str (e) else : error_mesasge = "äºæãã¬ãšã©ãŒãçºçããŸãã" with app: output_error = gr.Text(value=error_mesasge, label= "Error" ) app.launch(server_name= "0.0.0.0" , server_port= 7860 ) ããŒã«ã«ã§ã®åäœç¢ºèª ããŒã«ã«å®è¡ main.py ãšåããã£ã¬ã¯ããªã§ä»¥äžã®ã³ãã³ããå®è¡ããããšã§ãããŒã«ã«ãã¹ãïŒ127.0.0.1ïŒã®ããŒã 7860 ã§ Web ã¢ããªãèµ·åããŸãã $ python3 main.py Running on local URL: http:// 127.0 . 0.1 : 7860 ããŒã«ã«ã§èµ·åããã¢ããªãžæ¥ç¶ ããŒã«ã«ã§èµ·åãã Web ã¢ããªã® URLïŒ http://127.0.0.1:7860 ïŒã«ãã©ãŠã¶ã§ã¢ã¯ã»ã¹ããŠãé¡äŒŒç»åçæ Web ã¢ããªã«æ¥ç¶ããŸãã åæè¡šç€ºç»é¢ Google Cloud ãžã®ããã〠Cloud Run ã®äœ¿çš éçºããé¡äŒŒç»åçæ Web ã¢ããªããGoogle Cloud äžã«ãããã€ããŸããåœèšäºã§ã¯ãããã€å
ã®ãµãŒãã¹ãšããŠããµãŒããŒã¬ã¹ ã³ã³ãã ã³ã³ãã¥ãŒãã£ã³ã°ãµãŒãã¹ã§ãã Cloud Run ã䜿çšããŸããCloud Run ã®è©³çްã«ã€ããŠã¯ä»¥äžã®èšäºããäžèªãã ããã blog.g-gen.co.jp ãã£ã¬ã¯ããªæ§æ ä»åéçºããç»åçæ Web ã¢ããªã®ãã£ã¬ã¯ããªæ§æã¯ä»¥äžã®ãšããã§ãã imagen-app |-- main.py |-- requirements.txt |-- Dockerfile Dockerfile ã®äœæ Cloud Run ãžã®ãããã€ã«ã¯ Docker ã€ã¡ãŒãžãçšæããå¿
èŠããããããDockerfile ãäœæããŸãã FROM python:3.12-slim WORKDIR /usr/src/app COPY requirements.txt ./ RUN pip install --no-cache-dir -r requirements.txt COPY . . EXPOSE 7860 CMD [ "python", "./main.py" ] Cloud Run ã«ããã〠Dockerfile ã®ååšãããã£ã¬ã¯ããªã§ä»¥äžã® gcloud ã³ãã³ããé æ¬¡å®è¡ããŸãã ç°å¢å€æ° PROJECT_ID ã«å®çŸ©ãã Your-Project-ID ã®éšåã¯ããèªèº«ã䜿çšãã Google Cloud ãããžã§ã¯ãã® IDã«çœ®ãæããŠãã ããã # ç°å¢å€æ°ã®èšå® PROJECT_ID =Your-Project-ID REGION =asia-northeast1 SA_NAME =similar-image-generation # ãµãŒãã¹ã¢ã«ãŠã³ãäœæ gcloud iam service-accounts create $SA_NAME \ --description =" é¡äŒŒç»åçæWebã¢ããªçš " \ --display-name =" é¡äŒŒç»åçæWebã¢ã㪠" # ãµãŒãã¹ã¢ã«ãŠã³ããžæš©éä»äž gcloud projects add-iam-policy-binding $PROJECT_ID \ --member =" serviceAccount: $SA_NAME @ $PROJECT_ID .iam.gserviceaccount.com " \ --role =" roles/aiplatform.user " gcloud projects add-iam-policy-binding $PROJECT_ID \ --member =" serviceAccount: $SA_NAME @ $PROJECT_ID .iam.gserviceaccount.com " \ --role =" roles/logging.logWriter " # Cloud Run ãµãŒãã¹ãããã〠gcloud run deploy similar-image-generation --source . \ --region = asia-northeast1 \ --allow-unauthenticated \ --port 7860 \ --memory = 1Gi \ --min-instances = 1 \ --max-instances = 1 \ --service-account = $SA_NAME @ $PROJECT_ID .iam.gserviceaccount.com \ --set-env-vars = PROJECT_ID = $PROJECT_ID , LOCATION = $REGION ãã«ããããã³ã³ããã€ã¡ãŒãžã¯ãæå®ãããªãŒãžã§ã³ã«èªåã§äœæããããcloud-run-source-deployããšããååã® Artifact Registory ãªããžããªã«æ ŒçŽãããŸãã åè : ソースコードからデプロイする | Cloud Run Documentation | Google Cloud åäœç¢ºèª Cloud Run ã®ãããã€ãå®äºãããšãæšæºåºåã« Cloud Run ã®ãšã³ããã€ã³ãã Service URL ãšããŠåºåãããŸãããã® URL ã«ããã©ãŠã¶ããã¢ã¯ã»ã¹ããŸãã $ gcloud run deploy similar-image-generation --source . \ --region = asia-northeast1 \ --allow-unauthenticated \ --port 7860 \ --memory = 1Gi \ --min-instances = 1 \ --max-instances = 1 \ --service-account = $SA_NAME @ $PROJECT_ID .iam.gserviceaccount.com \ --set-env-vars = PROJECT_ID = $PROJECT_ID , LOCATION = $REGION Building using Dockerfile and deploying container to Cloud Run service [ similar-image-generation ] in project [ Your-Project-ID ] region [ asia-northeast1 ] OK Building and deploying... Done. OK Uploading sources... OK Building Container... Logs are available at [ https://console.cloud.google.com/cloud-build/builds/xxx?project = yyy ] . OK Creating Revision... OK Routing traffic... OK Setting IAM Policy... Done. Service [ similar-image-generation ] revision [ similar-image-generation-00007-zxh ] has been deployed and is serving 100 percent of traffic. Service URL: https://similar-image-generation-XXXXXXXXX-an.a.run.app ã¢ã¯ã»ã¹ã§ããããã²ãšãšããã®åäœç¢ºèªãããŠãã ããã é¡äŒŒç»åãçæ Cloud Run ã®ã¢ã¯ã»ã¹å
å¶åŸ¡ã«ã€ã㊠Cloud Run ã«ãããã€ãã Web ã¢ããªã®ã¢ã¯ã»ã¹å
å¶åŸ¡ãè¡ãããå ŽåãCloud Run ã®å段ã«ããŒããã©ã³ãµãŒãé
眮ããIdentity Aware ProxyïŒIAPïŒã«ãã IAM èªèšŒã Cloud Armor ã«ãã IP ã¢ãã¬ã¹ã®å¶éãå®è£
ããããšãã§ããŸãã 以äžã®èšäºããåç
§ãã ããã blog.g-gen.co.jp çŠäº éä¹ (èšäºäžèЧ) ã«ã¹ã¿ããŒãµã¯ã»ã¹èª² ãšã³ãžã㢠2024幎2æ G-gen JOIN å
ã¯ã¢ããªã±ãŒã·ã§ã³ãšã³ãžãã¢(ã€ã³ãã©ã¯AWS)ãšããŠãPM/PLã»äžæµå·¥çšãæ
åœãG-genã®Google Cloudãžã®ç±éãGoogle Cloudã®é
åãå³ãããªããæ¥ã
粟é²