□概要
chatGPT×DeepLの組み合わせで業務が爆発的に加速するイメージをみんなに持ってもらう記事。
僕はもう自分が仕事してるのかChatGPTが仕事してるのかわからないぐらい使い倒してます。
□注意
業務用コード/業務特有の要件をそのまま突っ込むのはコンプラ的に NGです。
質問を投げるときは一般的な内容に落とし込んでから聞きましょう。
□ユースケース
・SQLの生成
・正規表現の生成
・一般的な機能を実現するコードの生成
・ちょっとした可視化のコードの生成
・テストケース・注意事項などの洗い出し...等
業務上の要件の匂いがない、または簡単に匂いを消せるような問題に遭遇した時、
僕の中のファーストチョイスはもうGoogle検索エンジンではなくChatGPTになりました。
□実用例
□ケース1
一般的な機能を実現するフロントコードの生成
□状況
・サーバレス環境で小規模な機械学習推論を行いたい
・フロントはreactでバックエンドはAPIgateway+Lambda
・Lambdaのタイムアウト上限15分はかなり余裕があるが、
・APIgatewayのタイムアウトが上限29秒なため推論結果をフロントにレスポンスできない
・そこで以下のような対処法を考えた:
・新規lambda作成:
元のlambdaを非同期で呼び出してすぐレスポンス返す。
・元lambda修正:
成功したときは今まで通り普通に推論結果csvをS3(/{hotel_id}/data/temp/以下)に書き出し、
失敗した時にerror内容を書いたlogファイルをS3(/error/以下)に書き出すように。
・フロント修正:
推論リクエストを送った1秒後からファイル名ベースでS3ポーリング開始に修正。
送った1秒後以降の時間+推論実行者ID(?)をファイル名に含む推論結果csvまたは
エラーlogファイルのいずれかを発見したらフロント側で相応の画面を表示。
□実演
1.上の要件を丸ごとDeepLに突っ込むと英文が出てくる
2.英文を丸ごとChatGPTに投げる(とコードが出てくる、以下略)
コード
import React, { useState, useEffect } from "react";
import { Storage } from "aws-amplify";
function App() {
const [inferenceResult, setInferenceResult] = useState("");
const [errorLog, setErrorLog] = useState("");
const fileName = "example.csv"; // replace with the actual file name
useEffect(() => {
const interval = setInterval(() => {
checkS3();
}, 1000);
return () => clearInterval(interval);
}, []);
const checkS3 = async () => {
try {
const result = await Storage.get(`/{hotel_id}/data/temp/${fileName}`);
setInferenceResult(result);
} catch (error) {
// file not found, check error log
try {
const log = await Storage.get(`/error/${fileName}.log`);
setErrorLog(log);
} catch (error) {
// error log not found, do nothing
}
}
};
return (
<div>
{inferenceResult ? (
<div>
{/* display inference result */}
<h1>Inference Result</h1>
<pre>{inferenceResult}</pre>
</div>
) : errorLog ? (
<div>
{/* display error log */}
<h1>Error Log</h1>
<pre>{errorLog}</pre>
</div>
) : (
<div>
{/* display loading screen */}
<h1>Loading...</h1>
</div>
)}
</div>
);
}
export default App;
3.「hogehoge buttonを押した時にcheckS3が走るように,あとaws-amplifyの代わりにaws-sdkを使ってくれ〜」と投げる
コード
import React, { useState } from "react";
import AWS from "aws-sdk";
AWS.config.update({
region: "us-west-2", // replace with the actual region
credentials: {
accessKeyId: "YOUR_ACCESS_KEY_ID",
secretAccessKey: "YOUR_SECRET_ACCESS_KEY",
},
});
const s3 = new AWS.S3();
function App() {
const [inferenceResult, setInferenceResult] = useState("");
const [errorLog, setErrorLog] = useState("");
const fileName = "example.csv"; // replace with the actual file name
const checkS3 = async () => {
try {
const result = await s3
.getObject({
Bucket: "YOUR_BUCKET_NAME",
Key: `/{hotel_id}/data/temp/${fileName}`,
})
.promise();
setInferenceResult(result.Body.toString());
} catch (error) {
// file not found, check error log
try {
const log = await s3
.getObject({
Bucket: "YOUR_BUCKET_NAME",
Key: `/error/${fileName}.log`,
})
.promise();
setErrorLog(log.Body.toString());
} catch (error) {
// error log not found, do nothing
}
}
};
const handleClick = () => {
checkS3();
};
return (
<div>
<button onClick={handleClick}>hogehoge-button</button>
{inferenceResult ? (
<div>
{/* display inference result */}
<h1>Inference Result</h1>
<pre>{inferenceResult}</pre>
</div>
) : errorLog ? (
<div>
{/* display error log */}
<h1>Error Log</h1>
<pre>{errorLog}</pre>
</div>
) : (
<div>
{/* display loading screen */}
<h1>Click the button to check S3</h1>
</div>
)}
</div>
);
}
export default App;
4.「buttonコンポーネントはmaterial-uiを使って、あとjavascriptじゃなくてtypescriptで頼む〜」と投げる
コード
import React, { useState } from "react";
import AWS from "aws-sdk";
import Button from "@material-ui/core/Button";
AWS.config.update({
region: "us-west-2", // replace with the actual region
credentials: {
accessKeyId: "YOUR_ACCESS_KEY_ID",
secretAccessKey: "YOUR_SECRET_ACCESS_KEY",
},
});
const s3 = new AWS.S3();
function App(): JSX.Element {
const [inferenceResult, setInferenceResult] = useState<string>("");
const [errorLog, setErrorLog] = useState<string>("");
const fileName = "example.csv"; // replace with the actual file name
const checkS3 = async (): Promise<void> => {
try {
const result = await s3
.getObject({
Bucket: "YOUR_BUCKET_NAME",
Key: `/{hotel_id}/data/temp/${fileName}`,
})
.promise();
setInferenceResult(result.Body.toString());
} catch (error) {
// file not found, check error log
try {
const log = await s3
.getObject({
Bucket: "YOUR_BUCKET_NAME",
Key: `/error/${fileName}.log`,
})
.promise();
setErrorLog(log.Body.toString());
} catch (error) {
// error log not found, do nothing
}
}
};
const handleClick = (): void => {
checkS3();
};
return (
<div>
<Button variant="contained" color="primary" onClick={handleClick}>
hogehoge-button
</Button>
{inferenceResult ? (
<div>
{/* display inference result */}
<h1>Inference Result</h1>
<pre>{inferenceResult}</pre>
</div>
) : errorLog ? (
<div>
{/* display error log */}
<h1>Error Log</h1>
<pre>{errorLog}</pre>
</div>
) : (
<div>
{/* display loading screen */}
<h1>Click the button to check S3</h1>
</div>
)}
</div>
);
}
export default App;
5.「inferenveResultの中身はこんな感じ(mock定義)なのでTanstack react-tableを使ってその中身をテーブル表示してくれ、あとカラム定義はファイルから動的にやってくれ〜」と投げる
質問文章&コード
・コード
import React, { useState } from "react";
import AWS from "aws-sdk";
import Button from "@material-ui/core/Button";
import Table from "@material-ui/core/Table";
import TableBody from "@material-ui/core/TableBody";
import TableCell from "@material-ui/core/TableCell";
import TableContainer from "@material-ui/core/TableContainer";
import TableHead from "@material-ui/core/TableHead";
import TableRow from "@material-ui/core/TableRow";
import Paper from "@material-ui/core/Paper";
import { useTable } from "react-table";
AWS.config.update({
region: "us-west-2", // replace with the actual region
credentials: {
accessKeyId: "YOUR_ACCESS_KEY_ID",
secretAccessKey: "YOUR_SECRET_ACCESS_KEY",
},
});
const s3 = new AWS.S3();
interface Data {
[key: string]: string;
}
function App(): JSX.Element {
const [inferenceResult, setInferenceResult] = useState<string>("");
const [errorLog, setErrorLog] = useState<string>("");
const fileName = "example.csv"; // replace with the actual file name
const columns = React.useMemo(() => {
if (inferenceResult) {
const header = inferenceResult.trim().split("\n")[0];
return header.split(",").map((name) => ({
Header: name,
accessor: name,
}));
} else {
return [];
}
}, [inferenceResult]);
const data = React.useMemo<Data[]>(() => {
if (inferenceResult) {
const lines = inferenceResult.trim().split("\n");
const header = lines[0].split(",");
return lines
.slice(1) // skip header row
.map((line) => {
const values = line.split(",");
const obj: Data = {};
header.forEach((name, i) => {
obj[name] = values[i];
});
return obj;
});
} else {
return [];
}
}, [inferenceResult]);
const tableInstance = useTable({ columns, data });
const checkS3 = async (): Promise<void> => {
try {
const result = await s3
.getObject({
Bucket: "YOUR_BUCKET_NAME",
Key: `/{hotel_id}/data/temp/${fileName}`,
})
.promise();
setInferenceResult(result.Body.toString());
} catch (error) {
// file not found, check error log
try {
const log = await s3
.getObject({
Bucket: "YOUR_BUCKET_NAME",
Key: `/error/${fileName}.log`,
})
.promise();
setErrorLog(log.Body.toString());
} catch (error) {
// error log not found, do nothing
}
}
};
const handleClick = (): void => {
checkS3();
};
return (
<div>
<Button variant="contained" color="primary" onClick={handleClick}>
hogehoge-button
</Button>
{inferenceResult ? (
<div>
<TableContainer component={Paper}>
<Table {...tableInstance.getTableProps()}>
<TableHead>
{tableInstance.headerGroups.map((headerGroup) => (
<TableRow {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column) => (
<TableCell {...column.getHeaderProps()}>
{column.render("Header")}
</TableCell>
))}
</TableRow>
))}
</TableHead>
<TableBody>
{tableInstance.rows.map((row) => {
tableInstance.prepareRow(row);
return (
<TableRow {...row.getRowProps()}>
{row.cells.map((cell) => (
<TableCell {...cell.getCellProps()}>
{cell.render("Cell")}
</TableCell>
))}
</TableRow>
);
})}
</TableBody>
</Table>
</TableContainer>
</div>
) : errorLog ? (
<div>{errorLog}</div>
) : (
<div>Waiting for result...</div>
)}
</div>
);
}
export default App;
6.実際に動かして細かいところを修正
従来の方法で一から書いてたら2時間ぐらいかかるかもだったところまで12分で到達しました。
大抵の場合どうしても細かいミスは残りますが、あと一息のところまでは持っていけます。
□ケース2
機械学習タスクの評価指標の選定と、その場合のデータ取り扱いの注意点の相談
□状況
・画像約12,000枚を約300クラスに分類する画像分類タスクがある
・False Positiveを起こすとかなり致命的なタスクである
・評価指標はFβ-score(F1scoreに重みづけしてFPまたはFFに偏重させられるもの)を考えている
・その際にβ(重み)をどう決めればいいか知りたい
・あとデータ拡張を使おうとしており、その際のdata_splitの注意点も知りたい
・この辺を全部調査したい
□実演
1.質問を順番に英訳しながら聞くとこんな答えが返ってくる
・FP重視するならFβ-score使うのはナイスアイデア。
・でも後でそれぞれのクラスに対する推論パフォーマンスがわかるよう可視化した方がいい。
・βの値の設定については要件次第。例えば2とか5,あるいはそれ以上を設定していい塩梅を捉えろ。
・データ拡張を使うなら、まずtrain, val, testに分けた後で、trainに対してだけ拡張を当てるべき。
検証やテストデータに拡張データを含めるのは過学習につながる。
・データスプリットは各クラスの画像の数がなるべく均等になるよう分けるべき。
これも従来の方法で調べていたら30分ぐらいかかるかもだったところまで3分で到達できています。
所感
ネットの受け売りですが、ChatGPTのことは「年収1,000~2,000レベルの知見と経験を持った、絶対メンタルが壊れない、迅速にどんなタスクでも対応してくれる専属の部下」だと思うのが良さそうです。
まだChatGPTについてポンコツな回答やウィットに富んだネタ回答が返ってくる例を見て笑ってるぐらいの関わり方の人はもったいないです。ちょっと指示の仕方を工夫すれば、大抵の場合ちゃんとした回答が数秒で返ってきますし、信じられないほど業務効率が上がります。
というかそもそも人間やその辺の技術ブログからもポンコツな出力が出ることは全然ありますよね。自分の胸に手を当てて振り返ってみてください。ChatGPTより全然ポンコツなミスを重ねてきて今のあなたがいるはずです。それがわかったらもっとChatGPTを信用してあげてください。
Wited by H.K(20代/入社4年目)
※この記事は社内向けに約1年前に書かれたものです