Every SEAS student hits this wall
This was the final project for CSCI 6366, Neural Networks and Deep Learning at GWU's School of Engineering and Applied Science. A team project with my classmate Anurag Dhungana. We had a semester to build something real using what we'd learned, and we wanted to pick a problem we actually felt.
The problem we picked was one every SEAS student has hit: trying to figure out which courses you need before you can take the one you actually want. The official GWU systems are fragmented, the course bulletin is a PDF, the schedule is a separate portal, and prerequisite chains require you to manually trace through multiple pages. We wanted to build something that could answer the question naturally: "What do I need to take before CSCI 6364?"
Why fine-tuning alone invented prerequisites
The obvious first move was fine-tuning a language model on course data, and we did that. It handled simple questions, but it broke down on anything that required tracing a chain. A fine-tuned model doesn't reason over structured relationships so much as pattern-match on text that looks similar, so it would give you a confident wrong answer about prerequisites that didn't exist.
Course relationships are a graph problem wearing a text costume.
Prerequisites form a directed acyclic graph. If you want to answer multi-hop questions correctly, you need something that can walk that structure, not just retrieve text that sounds nearby. That's what pushed us toward building a knowledge graph.
System architecture
scraped
GWU schedule
my.gwu.edu · Spring 2026 · 586 rows
scraped
Course bulletin
CSCI + DATS · 187 courses
NetworkX DiGraph
Knowledge Graph
Nodes: course · professor · topic — Edges: prerequisite · taught_by · covers_topic (489 nodes / 566 links)
Student question
“What do I need before CSCI 6364?”
serialized subgraph
Structured context
The resolved prerequisite chain, as text
Unsloth · LoRA
Llama 3.1 8B (4-bit)
Fine-tuned on 154 graph-grounded multi-hop examples; r=32
answer
Grounded natural-language reply
The separate force-graph explorer lets you trace the path yourself
A knowledge graph that does the reasoning first
We scraped and structured data from two sources: the GWU CSCI and DATS course bulletin (187 courses) and the Spring 2026 course schedule (586 instances). From that, we built a knowledge graph in NetworkX where nodes are courses and edges are prerequisite relationships. spaCy handled the entity extraction from the bulletin text.
The graph became the backbone for our QA system. At query time, instead of handing the question straight to the model, we first traversed the graph to resolve prerequisite chains, then passed that context to the language model. The model's job changed from "figure out what the prerequisites are" to "explain the prerequisites I've already found."

Fine-tuning with LoRA
For the language model, we fine-tuned Llama 3.1 8B using LoRA adapters via Unsloth. We generated 2,828 training Q&A pairs: a mix of simple factual questions and complex multi-hop questions. LoRA meant we weren't fine-tuning all 8 billion parameters. We were adapting a small set of low-rank matrices, which made the whole thing feasible on academic resources. The graph-grounded run we trained later used only a small fraction of those pairs and finished in about 4 minutes, against 108 for our longest standard run, although a faster GPU accounts for much of that gap.
Four attempts, four lessons
We didn't get to the Knowledge Graph approach immediately. We went through four approaches across the semester.
The first two were standard fine-tunes without graph augmentation, a baseline run and a longer optimized run. These handled factual lookups reasonably well but failed on relational reasoning. On a test set of simple factual questions they scored 26% and 38% respectively.
The third approach introduced the Knowledge Graph as a retrieval mechanism but didn't train the model with graph-augmented examples. Its answers were inconsistent, and we never recorded a comparable score for it.
The fourth approach trained the model on graph-grounded examples so it would learn to use the retrieved context. It scored around 34%, but on a harder set of multi-hop questions, and it got there on a fraction of the training pairs the other runs used. Because the standard runs and the graph-grounded run were graded on different test sets, these numbers aren't a like-for-like comparison, and I can't claim from them that the graph beat the standard runs. A later evaluation file in the repo, which scored only those two runs, actually gives the graph-grounded run a lower F1 (0.17) than the optimized standard run (0.47).
So the case for the graph rests on what happens at answer time, where the model is handed an actual prerequisite chain instead of being left to invent one. We never measured hallucination directly, though, so that is a design argument rather than a benchmark result.
Designing for a system that's sometimes wrong
Building the frontend raised a design problem the model work didn't: how do you build an interface for a system that's partially correct, where the stakes of being wrong are real? A student asking about prerequisites might use the answer to decide what to register for.
The choice to use a chat interface rather than a search box was deliberate. Chat implies reasoning rather than fact retrieval, which is more honest for this system. More practically, chat lets students ask in the language they already use. "What do I need before 6364?" is a question a student would ask an advisor. Forcing that into a structured query form adds work for no real benefit.
I designed the answer card to treat graph traversal queries differently from simple factual lookups. A factual lookup would get a compact answer, while a traversal query would show which part of the graph was traversed and what relationships were resolved before the model generated its answer, which conveys model confidence through design without asking a student to interpret a probability score. For anything touching graduation requirements or prerequisite chains, the card was also meant to carry a prompt to confirm with an academic advisor, as prominent as the answer itself rather than tucked at the bottom.
Neither of those made it into the chat we shipped, and the live chat is currently switched off because we don't have the resources to host inference, so what you can try today is the standalone knowledge graph explorer.
What the graph changed, and what we couldn't measure
Every run started from the same Llama 3.1 8B base. What the graph changed was the context the model was handed at answer time: a real prerequisite chain instead of nothing.
This project was my first time working with knowledge graphs seriously, and the thing that stuck with me was how much the data representation matters. The graph-grounded model trained on far fewer examples than the others, and structuring the knowledge as a traversable graph meant it could be handed a real prerequisite chain at answer time. We never measured hallucination directly, though, and our scores don't prove the graph version was more accurate. If I ran this again, I'd test every approach on the same held-out multi-hop questions and count invented prerequisites as their own metric.
That insight carried into the interface. Because the data was structured as a graph, the interface could show graph-level concepts to users. The interactive visualization was only possible because the underlying data had that shape. If course relationships had been stored as unstructured text or a flat table, there would have been nothing useful to show.
Data architecture and UX are not separate decisions. How you structure your data determines what your interface can show.



