※ ↓ 元の記事 ↓
大学のプログラミング演習の講義で、GitHub Classroom を活用した課題の自動採点システムを導入しました。この記事では、その経緯、目的、そして実際の運用について紹介します。
昨年、私が所属する大学でプログラミング演習の SA (Student Assistant) を担当することになりました。その業務の一つに、学生が提出したプログラムの採点がありました。従来は、大学提供の授業管理支援システムに学生がプログラムを提出し、それを教員側で一括ダウンロードした後、手動で採点スクリプトを実行するという流れでした。
しかし、この方法にはいくつかの課題がありました。
これらの課題を解決するために、GitHub Classroom を用いた自動採点システムの導入を目指しました。このシステムは 2024 年度の講義で実際に導入・運用したところ導入までは時間がかかりましたが、一度導入できると凄く楽になったので、2025 年度も継続して利用することが決定しました。
GitHub Classroom を活用した自動採点の記事やノウハウはまだ少ないように感じたので、2024 年度に私たちが試行錯誤した経験を、この記事で共有していきます!
GitHub Classroom 上の課題の進捗状況
今回構築した自動採点システムは、GitHub Classroom を中心的な役割として利用しています。GitHub Classroom は、教師や教育機関がオンライン上で課題の作成、配布、管理を行うことを支援するツールです。GitHub Classroom についての詳細は、以下の公式ドキュメントやサイトをご参照ください。
自動採点の全体的な流れは、以下の図に示す通りです。
自動採点までの大体の流れ
この図の流れを、各ステップに分けてもう少し詳しく見ていきましょう。
まず、各課題の雛形となる「Template リポジトリ」を GitHub 上に作成します。このリポジトリには、課題の初期コード、採点用のスクリプト、テストケースなど、課題に必要な全ての要素を含めておきます。自動採点を実行するための GitHub Actions のワークフローファイルは、Template ファイルに予め含める必要はなく、この後の課題作成ステップをすると自動的に設定ファイルが作成されます。この Template リポジトリを用意することで、学生全員に同じ環境と課題設定を配布することが可能になります。
次に、GitHub Classroom の機能を使って「Assignment(課題)」を作成します。このステップで、ステップ1で作成した Template リポジトリを Classroom 内の課題と紐づけ、学生がアクセスできる招待URLを発行します。
Assignment を作成する際には、GUI または YAML ファイルを用いて、以下のような詳細な設定を行うことができます。
Private
または Public
を選択。他の学生の解答が見えてしまうと問題があるため、ここは Private
を選択。.github/workflows/classroom.yml
や採点用スクリプトなど)を指定できます。学生がこれらのファイルを変更しようとすると、教員側に通知が届くように設定でき、不正な変更を防ぐ助けになる。これらの設定を課題ごとに適切に行うことで、柔軟な自動採点環境を構築できます。
Assignment(課題)を作成すると、学生を招待するための専用URLが生成されます。学生がURLにアクセスして自身の GitHub アカウントで認証すると、課題用の Template リポジトリがコピーされ、学生専用の Private なリポジトリが自動的に作成されます。
学生はこの自分専用のリポジトリ上で課題に取り組み、プログラムを作成・編集してリモートリポジトリにプッシュすると、この push
操作がトリガーとなり、予め .github/workflows/classroom.yml
に定義された GitHub Actions のワークフローが自動実行されます。
ワークフロー内では、コードのビルド、テスト、採点が行われ、その結果(成功/失敗、点数など)はリポジトリの Actions タブやコミット履歴から学生自身がリアルタイムで確認できます。これにより、学生は競技プログラミングのように、提出後後すぐにフィードバックを得て、試行錯誤しながら学習を進めることが可能です。
Actions の log を見ると採点状況が丸分かり
一方、教員側は生徒のプログラムが自動で採点がされ、採点に関する業務が楽になります。GitHub Classroom を用いると、各 Assignment における学生ごとの最終的な成績(自動採点結果)を CSV ファイルとして一括ダウンロードでき、成績管理に非常に便利です。
ただし、単純な配点(例:満点100点)では、CSV だけではどの課題で得点・失点したのか分かりにくいという問題があったため、成績が一目で分かりやすくなるよう、1つの Assignment 内に
例えば 123...N
のような桁の並びから、各学生の達成状況が一目瞭然となり、成績の集計や分析が格段に容易になりました。ちなみに、この配点方法だと小課題数
このように、学生側の提出フローから教員側の成績管理まで、GitHub Classroom と Actions を中心に一連の流れを構築し、運用上の工夫を加えることで、効率的な授業運営を実現しました。
ここでは、自動採点環境の構築・運用を進める中で直面したいくつかの問題点と、それらに対する我々の解決策について書いていきます。
GitHub Classroom を利用した自動採点では、課題ごとに「Template リポジトリ」を用意する必要があります。しかし、そもそも今回の自動化導入の大きな目的の一つが「教員側の負担軽減」であったにも関わらず、この Template リポジトリの準備作業自体が煩雑で時間がかかるようでは、本末転倒になってしまいます。特に、採点スクリプト、GitHub Actions のワークフロー、課題の初期コード、説明ファイルなどを毎回ゼロから作成したり、コピーして修正したりするのは非効率です。
そこで、「Template リポジトリを作るための Template」、いわば Meta-Template とも呼べるリポジトリを作成することで、この準備作業の効率化を図りました。
この Meta-Template リポジトリには、以下のような汎用的な要素をあらかじめ含めています。
Meta-Template 内には Makefile を用意し、いくつかの定型的な準備作業を自動化するコマンドを定義しました。これにより、いくつかの簡単な設定値(例えば課題数など)を与えるだけで、必要なファイル生成や設定がよりスムーズに行えるようになりました。
具体的には、以下のような作業を make
コマンドで実行できるようにしています。
自動採点を導入する上で、教員側が最も頭を悩ませたのが「テストケースの期待出力(答え)をどのように管理し、学生のカンニングを防ぐか」という問題でした。単純に Template リポジトリ内に答えのファイルをそのまま含めると、学生が容易にアクセスできてしまいます。
この問題に対して、私たちはいくつかの対策案を検討しました。
解答とテストケースを別プライベートリポジトリに分離する
採点ロジックと解答をコンテナイメージに含める
外部の採点APIサーバーを利用する
これらの方法はいずれも有効ですが、設定・運用の複雑さが伴います。私たちの目的は、できるだけシンプルに、かつ教員側の負担を増やさずに自動採点を実現することだったので、「学生に見られても問題ない形式で答えをリポジトリ内に含める」という、よりシンプルなアプローチを採用することにしました。それが答えのハッシュ化です。
具体的には、各テストケースの期待出力を事前に計算し、その出力文字列に対して SHA-256 などのハッシュ関数を適用した結果(ハッシュ値)を Template リポジトリに含めておきます。そして、GitHub Actions での採点時には、学生のプログラムの実際の出力に対しても同じハッシュ関数を適用し、事前に計算しておいたハッシュ値と一致するかどうかで正誤を判定します。学生はリポジトリ内にあるハッシュ値を見ても、それが具体的にどのような出力に対応するのか直接知ることはできないため、外部リポジトリ、コンテナ、API サーバーなどの追加のインフラ管理や複雑な設定を必要とせず、比較的シンプルにカンニング対策をしました。
ただし、このハッシュ化方式にも限界があり課題の出力が非常に単純な場合(例: "Yes" / "No"、"OK" / "NG"、非常に小さい整数値など)は、考えられる出力候補のハッシュ値を手元で計算し、リポジトリ内のハッシュ値と比較する 総当たり攻撃(ブルートフォース) によって、元の答えが推測されてしまう可能性があります。~~(ただ、ハッシュ値から元の答えを推測できる学生は、課題自体も余裕で解けそうなので、ある意味合格でも良いのかもしれませんが…)~~
GitHub Actions を用いた自動採点は非常に強力ですが、万能ではありません。主に二つの限界がありました。
一つ目は、レポート課題における考察や自由記述のような、文章の内容を評価する必要がある課題です。プログラムの出力と異なり、明確な「正解」が一つに定まらず、主観的な評価が必要となるため、自動採点での対応は困難です。現状では教員側が内容を確認して手動で採点するのが現実的と判断しました。
二つ目は、プログラムの出力として複数の正解パターンが存在しうる場合の扱いです。例えば、ソートアルゴリズムを実装する課題で、入力データに同じ値を持つ要素が含まれている場合、安定ソートと不安定ソートではそれらの要素の相対的な順序が変わる可能性があります。どちらも仕様を満たしていれば正解とすべきですが、正解パターン数が多いと全てのケースを用意する必要があり、非常に面倒くさいです。実際にこのようなケースがあり、その際は問題文に制約を追加することで、期待される出力パターンを一意に定めて (あるいはパターン数を減らして)、単一パターンの照合で自動採点を可能にしました。
GitHub Actions は非常に便利な機能ですが、特に Private リポジトリ で利用する場合、無料枠には実行時間の制限があります。GitHub Free プランの場合、月あたり 2,000 分 という上限が設けられています(Public リポジトリでの利用は無料です)。GitHub Classroom で作成される学生のリポジトリは通常 Private なので、この制限が適用されます。
当初、個々の自動採点にはタイムアウトを設定しているため、合計実行時間が上限を超えることはないだろうと楽観視していました。しかし、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 を活用した自動採点の導入を検討してみることをお勧めします!