

Huggingfaceの機械学習のモデルをLambda上でロードして処理を行い結果を返す関数の実装で以下の問題が生じました。

## 解消したい問題

OSError: [Errno 30] Read-only file system: '/home/sbx_user1051'のエラーを解消できません。  
CloudWatch Logsのログになります。これを解消したいです。

### 状態

ソースコードは後で掲載。  
POSTで音声ファイルをLambdaへ送り、受け取ったらその音声ファイルを機械学習のモデルで読み込み実行する関数があります。  
しかしPOSTするとクライアント側にはInternal Server Errorが生じ、Lambada上にはOSError: [Errno 30]
Read-only file system: '/home/sbx_user1051'がログの中に残っています。

### 試したこと

Lambdaではtmpファイル以外は書き込みや削除操作ができません。そのためファイル書き込みの処理はtmpに入れるかそもそも書き込みさせないように対処しました。

・そのためファイル上の一時的にファイル保存する処理そのものを消しました。  
・Payannoteの処理でHuggingfaceのキャッシュファイルが生成されるため/tmpの絶対パス指定でキャッシュがtmpフォルダへ保存されるようにする  
・Pyannoteの関数を実行する前にos.environ["TRANSFORMERS_CACHE"] =
"/tmp"でキャッシュをtmpへ保存されるようにする(念の為)  
・__pycache__ファイルを生成させないようにENV PYTHONDONTWRITEBYTECODE=1をDockerfileに記載する

上記対策をしたのですが上記対策のどこかが間違えている、及び他に要因があるのでしょうが思いつきませんでした。  
具体的には/home/sbx_user1051へ勝手に書き込みを始めることに起因したライブラリや処理の部分はどこになるのかこれ以上は検討できませんでした。  
そのためStackoverflowへ投稿することにしました。

## ソースコード

dspeaker_diarization.pyファイルのdiarization関数が今回の問題となっている箇所になります。クライアントからLambdaの
https://awsのURI/dirization へPOSTしていて生じます。  
Hello Worldを表示だけさせることはできたのでそもそもホスティングができていない、ということはありません。

**speaker_diarization.py**

[code]

    import os
    from pyannote.audio import Pipeline
    from fastapi import UploadFile
    import io
    from dotenv import load_dotenv
    from pydub import AudioSegment
    import numpy as np
    import torch
    
    
    def annotation_to_dict(annotation):
        result_dict = {}
        for segment, _, speaker in annotation.itertracks(yield_label=True):
            start = segment.start
            end = segment.end
            if speaker not in result_dict:
                result_dict[speaker] = []
            result_dict[speaker].append({"start": start, "end": end})
        return result_dict
    
    
    async def diarization(file: UploadFile):
        """スピーカー分離を実行"""
        load_dotenv()
        # 音声ファイルのバイナリデータを読み込む
        audio_data = await file.read()
        # バイナリデータを io.BytesIO オブジェクトに変換
        audio_buffer = io.BytesIO(audio_data)
        # Convert the audio_buffer to an AudioSegment object
        audio_segment = AudioSegment.from_file(
            audio_buffer, file.content_type.split("/")[-1]
        )
        # Get waveform data as a numpy array
        waveform_data = np.array(audio_segment.get_array_of_samples())
        # Convert the numpy array to a torch.Tensor and normalize it
        waveform_tensor = torch.tensor(waveform_data, dtype=torch.float32) / (1 << 15)
        # Reshape the tensor to match the number of channels in the audio_segment
        waveform_tensor = waveform_tensor = waveform_tensor.view(audio_segment.channels, -1)
    
        pipeline = Pipeline.from_pretrained(
            "pyannote/speaker-diarization@2.1",
            use_auth_token=os.environ["HUGGING_API_KEY"],
            cache_dir="/tmp",
        )
        # Pass the torch.Tensor with waveform data to the pipeline
        annotation = pipeline(
            {"waveform": waveform_tensor, "sample_rate": audio_segment.frame_rate}
        )
    
        result = annotation_to_dict(annotation)
    
    
        return result
    
[/code]

**app.py**

[code]

    """サーバー立ち上げ"""
    from fastapi import FastAPI, File, UploadFile
    from fastapi.middleware.cors import CORSMiddleware
    from fastapi.responses import JSONResponse
    import uvicorn
    import os
    from mangum import Mangum
    
    from speaker_diarization import diarization
    
    app = FastAPI()
    
    app.add_middleware(
        CORSMiddleware,
        allow_origins=["*"],
        allow_origin_regex="https?://.*",
        allow_credentials=True,
        allow_methods=["*"],
        allow_headers=["*"],
    )
    handler = Mangum(app)
    
    def hello_world(text: str):
        """テスト用"""
        return "hello world" + text
    
    
    @app.get("/")
    def my_function():
        """テスト用のパス"""
        greed = hello_world("test")
        return JSONResponse({"text": greed})
    
    
    @app.post("/diarization")
    async def post_diarization(file: UploadFile = File(...)):  # UploadFileを引数に追加
        """発話者区分を実行する関数"""
        os.environ["TRANSFORMERS_CACHE"] = "/tmp"
        res = await diarization(file)
    
        print({"text": res})
        return JSONResponse({"text": res})
    
    
    if __name__ == "__main__":
        uvicorn.run(app, host="0.0.0.0", port=9000)
    
[/code]


CC BY-SA 4.0
2023-05-12T04:18:15.367
94826
2023-05-16T00:57:32.340
2023-05-16T00:57:32.340
3060
58261
question
python python3 docker aws-lambda
PythonのDockerイメージで立てたLambdaの関数を実行すると [Errno 30] Read-only file system: '/home/sbx_user1051' になる