automatic-grading

大学講義の課題を自動採点してみた

Created: 2025-04-13
Updated: 2025-04-13

はじめに

※ ↓ 元の記事 ↓

Github Classroom で大学講義の課題を自動採点してみた

大学のプログラミング演習の講義で、GitHub Classroom を活用した課題の自動採点システムを導入しました。この記事では、その経緯、目的、そして実際の運用について紹介します。

昨年、私が所属する大学でプログラミング演習の SA (Student Assistant) を担当することになりました。その業務の一つに、学生が提出したプログラムの採点がありました。従来は、大学提供の授業管理支援システムに学生がプログラムを提出し、それを教員側で一括ダウンロードした後、手動で採点スクリプトを実行するという流れでした。

しかし、この方法にはいくつかの課題がありました。

  • 採点結果のフィードバック遅延: 教員がスクリプトを実行しない限り採点が行われず、学生が授業時間外に課題を提出した場合、結果を知ることができるのは次の授業以降になっていた。
  • 教員の採点負担: 採点作業には多くの時間と労力が割かれていた。

これらの課題を解決するために、GitHub Classroom を用いた自動採点システムの導入を目指しました。このシステムは 2024 年度の講義で実際に導入・運用したところ導入までは時間がかかりましたが、一度導入できると凄く楽になったので、2025 年度も継続して利用することが決定しました。

GitHub Classroom を活用した自動採点の記事やノウハウはまだ少ないように感じたので、2024 年度に私たちが試行錯誤した経験を、この記事で共有していきます!

GitHub Classroom 上の課題の進捗状況

自動採点の仕組み

今回構築した自動採点システムは、GitHub Classroom を中心的な役割として利用しています。GitHub Classroom は、教師や教育機関がオンライン上で課題の作成、配布、管理を行うことを支援するツールです。GitHub Classroom についての詳細は、以下の公式ドキュメントやサイトをご参照ください。

GitHub Classroom について - GitHub Docs

GitHub Classroom

自動採点の全体的な流れは、以下の図に示す通りです。

自動採点までの大体の流れ

この図の流れを、各ステップに分けてもう少し詳しく見ていきましょう。

1. 課題用のTemplateリポジトリを作成する

まず、各課題の雛形となる「Template リポジトリ」を GitHub 上に作成します。このリポジトリには、課題の初期コード、採点用のスクリプト、テストケースなど、課題に必要な全ての要素を含めておきます。自動採点を実行するための GitHub Actions のワークフローファイルは、Template ファイルに予め含める必要はなく、この後の課題作成ステップをすると自動的に設定ファイルが作成されます。この Template リポジトリを用意することで、学生全員に同じ環境と課題設定を配布することが可能になります。

2. Templateリポジトリを用いてAssignment(課題)を作成する

次に、GitHub Classroom の機能を使って「Assignment(課題)」を作成します。このステップで、ステップ1で作成した Template リポジトリを Classroom 内の課題と紐づけ、学生がアクセスできる招待URLを発行します。

Assignment を作成する際には、GUI または YAML ファイルを用いて、以下のような詳細な設定を行うことができます。

  • 課題名: 学生に表示される課題の名称。
  • 締切日: 課題の提出期限。締切時刻を過ぎると、学生のリポジトリへの書き込みアクセスを自動的に制限することも可能(後からの変更も可能)。
  • Template リポジトリ: この課題のベースとなる Template リポジトリ。
  • リポジトリの可視性: Private または Public を選択。他の学生の解答が見えてしまうと問題があるため、ここは Private を選択。
  • 自動採点設定: GitHub Actions を用いた自動採点に関する設定。
    • ビルドコマンド: (必要な場合) 採点前に実行するコンパイルなどのビルドコマンド。
    • テストコマンド: 採点を実行するためのコマンド。
    • 点数: 設定したテストがすべて成功した場合に与えられる点数。
    • タイムアウト時間: テスト実行の最大許容時間。無限ループなどで採点が停止しないように設定。
  • 保護されたファイル: 学生に編集されると困るファイル(例: .github/workflows/classroom.yml や採点用スクリプトなど)を指定できます。学生がこれらのファイルを変更しようとすると、教員側に通知が届くように設定でき、不正な変更を防ぐ助けになる。

これらの設定を課題ごとに適切に行うことで、柔軟な自動採点環境を構築できます。

3. 学生の利用フローと成績管理

Assignment(課題)を作成すると、学生を招待するための専用URLが生成されます。学生がURLにアクセスして自身の GitHub アカウントで認証すると、課題用の Template リポジトリがコピーされ、学生専用の Private なリポジトリが自動的に作成されます。

学生はこの自分専用のリポジトリ上で課題に取り組み、プログラムを作成・編集してリモートリポジトリにプッシュすると、この push 操作がトリガーとなり、予め .github/workflows/classroom.yml に定義された GitHub Actions のワークフローが自動実行されます。

ワークフロー内では、コードのビルド、テスト、採点が行われ、その結果(成功/失敗、点数など)はリポジトリの Actions タブやコミット履歴から学生自身がリアルタイムで確認できます。これにより、学生は競技プログラミングのように、提出後後すぐにフィードバックを得て、試行錯誤しながら学習を進めることが可能です。

Actions の log を見ると採点状況が丸分かり

一方、教員側は生徒のプログラムが自動で採点がされ、採点に関する業務が楽になります。GitHub Classroom を用いると、各 Assignment における学生ごとの最終的な成績(自動採点結果)を CSV ファイルとして一括ダウンロードでき、成績管理に非常に便利です。 ただし、単純な配点(例:満点100点)では、CSV だけではどの課題で得点・失点したのか分かりにくいという問題があったため、成績が一目で分かりやすくなるよう、1つの Assignment 内に 個の小課題がある場合、 番目の小課題の配点を 点としました。

例えば なら、満点は 点となります。これにより、CSV 上の総合点を見るだけで、123...N のような桁の並びから、各学生の達成状況が一目瞭然となり、成績の集計や分析が格段に容易になりました。ちなみに、この配点方法だと小課題数 が増えると合計点数もかなり大きくなります。私の講義では は最大9程度でしたが、その範囲では特に問題なく機能しました。

このように、学生側の提出フローから教員側の成績管理まで、GitHub Classroom と Actions を中心に一連の流れを構築し、運用上の工夫を加えることで、効率的な授業運営を実現しました。

問題点・解決策

ここでは、自動採点環境の構築・運用を進める中で直面したいくつかの問題点と、それらに対する我々の解決策について書いていきます。

Template リポジトリ作成の手間

GitHub Classroom を利用した自動採点では、課題ごとに「Template リポジトリ」を用意する必要があります。しかし、そもそも今回の自動化導入の大きな目的の一つが「教員側の負担軽減」であったにも関わらず、この Template リポジトリの準備作業自体が煩雑で時間がかかるようでは、本末転倒になってしまいます。特に、採点スクリプト、GitHub Actions のワークフロー、課題の初期コード、説明ファイルなどを毎回ゼロから作成したり、コピーして修正したりするのは非効率です。

そこで、「Template リポジトリを作るための Template」、いわば Meta-Template とも呼べるリポジトリを作成することで、この準備作業の効率化を図りました。

GitHub - azu-lab/AutoGrader-Template: A template repository for building automated grading systems for course assignments.

この Meta-Template リポジトリには、以下のような汎用的な要素をあらかじめ含めています。

  • ローカル採点スクリプト: 学生が自分のPC上で、提出前と同じ採点基準で動作確認を行えるようにするためのスクリプト。
  • 提出補助スクリプト: Git の操作に不慣れな学生でも、簡単なコマンドで課題を提出(commit & push)できるようにするためのスクリプト。
  • 基本的なファイル構造: 課題ファイルやテストケースを配置するためのディレクトリ構造。

Meta-Template 内には Makefile を用意し、いくつかの定型的な準備作業を自動化するコマンドを定義しました。これにより、いくつかの簡単な設定値(例えば課題数など)を与えるだけで、必要なファイル生成や設定がよりスムーズに行えるようになりました。

具体的には、以下のような作業を make コマンドで実行できるようにしています。

  • 課題数を指定すると、その課題に応じた採点スクリプトを生成する。
  • 模範解答のプログラムとテストケースの入力を用意しておくと、期待出力 (答え) を作成する。

学生に課題の答えを見られてしまう

自動採点を導入する上で、教員側が最も頭を悩ませたのが「テストケースの期待出力(答え)をどのように管理し、学生のカンニングを防ぐか」という問題でした。単純に Template リポジトリ内に答えのファイルをそのまま含めると、学生が容易にアクセスできてしまいます。

この問題に対して、私たちはいくつかの対策案を検討しました。

  • 解答とテストケースを別プライベートリポジトリに分離する

    • 方法: 解答ファイルや詳細なテストケース、採点スクリプト本体などを、教員のみがアクセス可能な別のプライベートリポジトリに格納し、採点ワークフロー内で、Personal Access Token (PAT) や Deploy Key を用いて解答リポジトリから必要なファイルを取得して採点する。
    • メリット: 学生は自分のリポジトリを見ただけでは解答が分からない。
    • デメリット: PAT 等のトークン管理が必要。また、ワークフローファイルを見れば、どのリポジトリからファイルを取得しているかが分かってしまう。
  • 採点ロジックと解答をコンテナイメージに含める

    • 方法: 解答、テストスイート、採点スクリプト、必要なライブラリ等を全て含んだ Docker イメージを作成・レジストリにプッシュしておき、Actions ワークフローでは、認証情報を使ってこのプライベートイメージをプルしてコンテナ内で学生のコードを採点する。
    • メリット: 実行環境の依存関係をまとめられ、解答がファイルとして直接 Actions 環境に展開されにくい。
    • デメリット: Docker イメージのビルド・管理やレジストリの設定が必要。
  • 外部の採点APIサーバーを利用する

    • 方法: 解答、テストケース、採点ロジックを外部サーバー(AWS Lambda, Cloud Functions, 自前サーバー等)に API として構築する。Actions ワークフローは学生のコードをこの API に送信し、採点結果を受け取る。
    • メリット: 解答が学生の環境から完全に隔離され、安全性が高い。
    • デメリット: 外部サーバーの構築・運用・保守の手間とコストが大きい。API キー等の管理も必要。

これらの方法はいずれも有効ですが、設定・運用の複雑さが伴います。私たちの目的は、できるだけシンプルに、かつ教員側の負担を増やさずに自動採点を実現することだったので、「学生に見られても問題ない形式で答えをリポジトリ内に含める」という、よりシンプルなアプローチを採用することにしました。それが答えのハッシュ化です。

具体的には、各テストケースの期待出力を事前に計算し、その出力文字列に対して SHA-256 などのハッシュ関数を適用した結果(ハッシュ値)を Template リポジトリに含めておきます。そして、GitHub Actions での採点時には、学生のプログラムの実際の出力に対しても同じハッシュ関数を適用し、事前に計算しておいたハッシュ値と一致するかどうかで正誤を判定します。学生はリポジトリ内にあるハッシュ値を見ても、それが具体的にどのような出力に対応するのか直接知ることはできないため、外部リポジトリ、コンテナ、API サーバーなどの追加のインフラ管理や複雑な設定を必要とせず、比較的シンプルにカンニング対策をしました。

ただし、このハッシュ化方式にも限界があり課題の出力が非常に単純な場合(例: "Yes" / "No"、"OK" / "NG"、非常に小さい整数値など)は、考えられる出力候補のハッシュ値を手元で計算し、リポジトリ内のハッシュ値と比較する 総当たり攻撃(ブルートフォース) によって、元の答えが推測されてしまう可能性があります。~~(ただ、ハッシュ値から元の答えを推測できる学生は、課題自体も余裕で解けそうなので、ある意味合格でも良いのかもしれませんが…)~~

自動採点の限界:主観評価と複数正解パターン

GitHub Actions を用いた自動採点は非常に強力ですが、万能ではありません。主に二つの限界がありました。

一つ目は、レポート課題における考察や自由記述のような、文章の内容を評価する必要がある課題です。プログラムの出力と異なり、明確な「正解」が一つに定まらず、主観的な評価が必要となるため、自動採点での対応は困難です。現状では教員側が内容を確認して手動で採点するのが現実的と判断しました。

二つ目は、プログラムの出力として複数の正解パターンが存在しうる場合の扱いです。例えば、ソートアルゴリズムを実装する課題で、入力データに同じ値を持つ要素が含まれている場合、安定ソートと不安定ソートではそれらの要素の相対的な順序が変わる可能性があります。どちらも仕様を満たしていれば正解とすべきですが、正解パターン数が多いと全てのケースを用意する必要があり、非常に面倒くさいです。実際にこのようなケースがあり、その際は問題文に制約を追加することで、期待される出力パターンを一意に定めて (あるいはパターン数を減らして)、単一パターンの照合で自動採点を可能にしました。

GitHub Actions の無料枠と実行時間

GitHub Actions は非常に便利な機能ですが、特に Private リポジトリ で利用する場合、無料枠には実行時間の制限があります。GitHub Free プランの場合、月あたり 2,000 分 という上限が設けられています(Public リポジトリでの利用は無料です)。GitHub Classroom で作成される学生のリポジトリは通常 Private なので、この制限が適用されます。

GitHub Actions の課金について - GitHub Docs

当初、個々の自動採点にはタイムアウトを設定しているため、合計実行時間が上限を超えることはないだろうと楽観視していました。しかし、GitHub Actions の課金仕様として、ジョブの実行時間が分単位で切り上げられる という点を見落としていました。つまり、たとえ1回の採点が10秒で終わったとしても、実行時間としては1分としてカウントされてしまいます。学生が頻繁に push を繰り返すと、実際の計算時間は短くても、累計の消費時間は思った以上に早く増加する可能性があります。特に学生数が多かったり、課題が頻繁にある・多い講義の場合、全員が何度も提出(push)を繰り返すと、月間の無料枠を使い切ってしまう可能性がありました。

この問題に対して、技術的な解決策をすぐに見つけるのは難しかったため、最初の講義で GitHub Actions の無料枠について簡単に説明し、可能な限りローカル環境に用意した採点スクリプトで十分にテストしてから提出して、無駄な push は控えるように 学生に協力を依頼しました。これは学生の善意に頼る方法ではありますが、結果的に我々の講義 (受講者 80 人前後、週 1 回の演習) では期間を通して無料枠の上限に達することはありませんでした。

講義全体の Actions 利用時間。講義は数カ月あるので結構余裕をもって採点できている

おわりに

今回、GitHub Classroom と GitHub Actions を活用した自動採点を導入して、競技プログラミングのようにしてみました。教員側は、採点作業の大部分が自動化され、負担が大幅に軽減、結果集計も容易になりました。学生は課題を提出すれば、ほぼリアルタイムで採点結果のフィードバックを受けられるようになり、試行錯誤しながら効率的に学習を進められるようになりました。

教員側も学生も Win-Win になれたので、もし大学などで TA (Teaching Assistant) や SA (Student Assistant) として講義運営のサポートをする機会があり、プログラミング課題の採点業務があるのであれば、GitHub Classroom を活用した自動採点の導入を検討してみることをお勧めします!