Teaching and Learning Online

Many teachers suffer from a cognitive bias known as the “Curse of Knowledge.” When you’ve mastered something, many aspects of that topic feel basic or intuitive. Especially, as time passes and you forget what it was like to initially struggle with a concept. To combat this, many of us take off our teacher hats from time to time and don our learner hats. We can do that in many ways. To that end, a few years ago I enrolled in a program called OMSCS: Georgia Tech’s Online Masters of Computer Science. This summer I took my last classes and graduated from the program. At the end of any valuable experience, it is worthwhile to reflect on it to extract the lessons you can carry with you.

Going into the OMSCS experience, there were some things I knew I would get out of it: a deeper understanding of many areas of computer science, the chance to take on the role of a learner in my field, and a chance to collaborate and learn alongside other working professionals. I also ended up not just thinking about the content taught to me but the way the professors delivered their courses in an online format. I didn’t expect the online aspect of it to be so relevant in my own teaching but here we are.

Here are a few lessons I learned beyond the content of my courses:

  • The urge to procrastinate is strong! With less face-to-face time with teachers, there feels like a little less accountability. Something that helps is breaking big tasks down into smaller ones. Having more deadlines or milestones can actually reduce stress as it leads to not putting off work longer.
  • Input = Output What you put into a class correlates to how much you get out of it. It can be tempting to just put in the minimal effort and check off the list of things you are required to do to get the grade but I find I got way more out of the classes where I looked into the supplemental resources provided, read beyond just the assigned chapters and tinkered with concepts.
  • Going off the point above: It’s okay to just survive! I did not (wasn’t able to) go above and beyond in every class, only the ones I felt compelled to do so. Especially now, there’s so much on our plates that just checking off the to-do list and making it to the next week is more than admirable
  • Over-communication > under-communication
    Yes, getting tons of emails is annoying and I’ve changed my settings on various platforms to reduce the number of notifications I receive. But, getting too much information has been an easier problem for me to deal with than getting too little information. I’d rather repeat myself multiple times and in different ways at the risk of sounding like a broken record than leave some behind and confused. With background stressors, there can be hidden factors that cause our attention to fade or to forget something we just heard. Having information in an email, on a class website, in a pre-recorded video, and repeated live is not just covering all our bases but maximizing the chance that it’s received by all. As a student, don’t be afraid to follow-up on an email a teacher hasn’t responded to after a few days: many can get lost in the ever-growing mountain we call our inbox. I know I’m guilty.
  • Relationships > Content
    Teaching and learning can be a fun, academic puzzle to solve. How do I express this idea? How do I explain it in a way others can understand? How can I design it in a way to guide someone to learn it themselves? But at the end of the day, the people doing the teaching and the people doing the learning are human beings with a lifetime of experiences, fears, joys, interests, biases, etc… Connecting with the people in our space (physical or virtual) is the real challenge of a school no matter your role. I remember the teammates I worked with on group projects more than the lectures on the principles of software engineering, artificial intelligence, databases, and security. I value the peer reviews, TA feedback and the time the professor admitted they were wrong in a forum post and showed me even experts in a field get mixed up sometimes
  • Fast, simple feedback loops drastically improve learning
    One of my favorite but most challenging classes was Artificial Intelligence. I learned more in that class than any other not only because of that challenge but due to the speed and quality of feedback. The professor set up a way for us to submit the code we wrote and have it tested and our results were given back to us quickly. So I could create an AI to battle another one and my score was based on its performance and when it did poorly I could attempt to figure out why and make improvements. In the classes I teach involving computer programming, I have a similar setup for some tasks but for many types of work, this isn’t possible. A lesson I take from this is to try to give and get as much feedback as you can! Peer review, asking for more feedback when you don’t get enough, and iterating on your work are all good ideas.
  • Finding a good balance of control is important
    Sometimes it’s nice to just get straightforward tasks and directions and do the work. Other times, it’s better to get a chance to be creative and come up with our own tasks. I think any good learning experience or course should have a mix of varying degrees of teacher and learner control. I try to structure my courses by giving specific tasks to learners with very explicit directions and feedback at first to build core skills then opening it up to allow students to take the skills they built in the directions they want to take them and chart their own courses of exploration. When I set out to learn a new skill, I start by following step by step tutorials and reading books or guides and then planning personal projects using the knowledge gained from that first stage of learning.

I have avoided talking about my specific content area too much since I think these lessons apply regardless of if you’re teaching/learning computer science or anything else. But I would like to speak a little to one of my favorite sub-fields of CS: game design. I have made so many connections between designing good games and crafting effective lessons and courses. You have to understand the person playing your game (or student learning your topic) and give them the appropriate level of challenge: too easy and they get bored, too complex before they’re ready and they get frustrated. We want to keep them in the zone where they’re just brushing up against the boundary of their current level of skill to push them to make it to the next level. We have to make it interactive and get them to get “in character” and take on the role of superhero, mathematician, problem solver, computer scientist, race car driver, whatever. It’s an iterative process. We try out our ideas, get feedback, and improve on it next time. There will be many failures as we navigate the world of virtual learning but that’s exciting. Because each “failure” is an opportunity to learn and to grow.

Collision Detection

I recently read a great explanation of the Gilbert-Johnson-Keerthi Distance Algorithm for Efficient Collision detection. I highly recommend checking it out. I’ve used collision detection as a way of teaching both math and computer programming concepts to students for a few years and would love to share some of the examples I’ve made with other educators and students.

What is Collision Detection?

I suppose, first I should explain what collision detection is. It’s basically checking to see if two shapes are overlapping at all:

Why would we care if two shapes are colliding or not? Well, there are several situations where this information is important: in video games to see if the player is being hit by an enemy, in physics simulations to check if two objects are bumping into one another or in robotics when interpreting sensor data.

Circles

Now that the answer to the “But, when are we gonna use this in the REAL world?” question is out of the way, how do we actually do it? Let’s just consider circles for now. What information do we need to figure out if two circle are colliding? See if you can figure it out by playing with the example below. Note that r1 and r2 are the radii of the circles.

What was the relationship between the distance and the sum of the radii when the circle are colliding? We can represent it in pseudocode:

if (distance < r1 + r2) then
circles are colliding
else
circles are not colliding
end if

Your inner mathematician might be saying “If they are equal, the circles intersect at a point, they should be colliding!” That’s fine, change the “less than” in the code above to a “less than or equal to” but with how floating point arithmetic works, it should be functionally equivalent. The only reason you can get the distance to equal 75 above is because I rounded the actual value to avoid the ugliness of a ton of numbers after the decimal point. Edge cases like that make for good classroom discussion.

To draw the circles to the screen we need to already know the radius and location of the center of each circle. But we will have to compute the distance ourselves. If you know the center of circle 1 is at (x1, y1) and the center of circle 2 is at (x2, y2) we can use the Pythagorean Theorem (or distance formula) to find the distance.

If we wanted to express that in pseudocode, we do something like:

dx = x2 - x1
dy = y2 - y1
distance = sqrt( dx*dx + dy*dy )

Above I used intermediate values of dx and dy to represent the difference in the x values and the difference in the y values. This isn’t required. We could have made this a one-liner but for the sake of clarity, I broke it down into multiple steps. I also just multiplied dx by itself to square it even though some languages have an exponentiation operator (** in python) or a power function like Math.pow().

Cool, that’s about all there is to that example. If you want to play around with the code yourself, here you go.

Spheres

I told you that collision detection is used in video games but aren’t more and more video games being made in 3D nowadays? We just looked at circles which are 2D. Let’s see if we can extend our approach to work in 3D by considering two spheres instead of two circles. This next toy has sliders to control the position of one of the spheres in the x, y and z direction. Try to make them collide. What information do we need to figure out if they are colliding? No numbers on screen provided this time:

You may have realized that the information we need is identical: the radius of each sphere and the distance between them. The same general method stated in the 2D case works for the 3D case. Of course, this time we’ve now got to consider the position of each sphere in the z direction. But luckily, we don’t have to tweak our distance formula very much to make it work:


Or, in pseudocode:

dx = x2 - x1
dy = y2 - y1
dz = z2 - z1
distance = sqrt( dx*dx + dy*dy + dz*dz )

if (distance < r1 + r2) then
spheres are colliding
else
spheres are not colliding
end if

For a good explanation of why this is, check out Math is Fun. But if tinkering with code sounds more fun to you, here it is.

Rectangles

Alright, 2D, 3D, we’ve got this! But, what if we want other shapes like rectangles or cubes? No radius in a rectangle, so our above method wouldn’t work. But, circles alone won’t always cut it. Consider the following situation:

With bounding circles, we might get a false positive and think two objects are colliding when they aren’t. You could shrink the circles around them to reduce the number of false positives but then you’d end up with false negatives:

Sometimes, rectangles will just be a better fit than circles for the objects we want to enclose. So, let’s try to figure out how to tell if two rectangles are colliding.


If you want spoilers, see how I did it in the code.

Unfortunately, there’s not an elegant mathematical formula like the Pythagorean Theorem that can help us out this time. We’ve got to do some logical deduction and come up with a set of rules. It may be easier to thinking about one dimension at a time.

In the diagram above, we see that we can draw the two line segments such that A is always to the left of B and C is to the left of D. The question of collision becomes about the relationship of the points on the red segment to the points on the blue segment. The rule to me seems to be that if A is to the left of D and B is to the right of C, the segments collide. This holds for these 3 examples. Try coming up with more and see if that rule holds or if you can find an exception.
The next challenge is to extend this from 1D to 2D.


In this diagram, imagine you’re only seeing one side of a rectangle for each of the given line segments. For two of the pairs, you could draw rectangles that would be colliding but for the first, you can’t. Try it if you don’t believe me.

Now, rotate these examples 90 degrees. You will see that the same rule above can tell us whether the projections of a rectangle onto either the x-axis or the y-axis collide. We have to combine all that to form a single conjunctive logical rule (by that, I mean several comparisons connected with “and”).


When drawing rectangles in code, you might have 4 points but more likely you just have one point (most commonly, the top left corner) and the width and height of the rectangle. We can still figure out the other points from that information give that these rectangles are axes aligned. For example, the bottom right corner of the first rectangle would be (x1+w1, y2+h2) and the top right corner of the second rectangle is (x2+w2, y2). This is enough information to allow us to write some pseudocode:

if (x1 < x2 + w2)
and (x1 + w1 > x2)
and (y1 < y2 + h2)
and (y1 + h1 > y2) then
rectangles are colliding
else
rectangles are not colliding
end if

This will likely take more work to convince yourself it’s true than the circles and spheres. Come up with examples, see if you can find any that this code fails on.

Many collisions

In a game or a simulation, it’s likely there are more than two objects on the screen at a time. So, we need to be able to take the concepts above and apply them repeatedly in a given scene. The example below isn’t interactive like the rest but keep an eye on how quickly the number of collisions adds up:


(As always, feel free to play with the code. Try changing the 3 values at the top and seeing what happens.)

One way you could handle all these collisions is by using an array to store all the objects and a loop to iterate through them all and another loop nested inside of that to check like this:

N = 15
circles = new Array(N)
//add circles to the array
index1 = 0
loop from 0 to N - 1
start = index1 + 1
index2 = index1 + 1
loop from start to N - 1
//check if circle[index1] is colliding with circle[index2]
index2 = index2 + 1
end loop
index = index + 1
end loop

That’s a lot of collision checks. The first circle is checked against the other N – 1 circles, then the 2nd circle is checked against the remaining N – 2 and so on. Another math connection here: arithmetic sequences and series. If we add it up, there would be (N2 – N) / 2 comparisons. There are definitely ways to do this more efficiently. If that interests you, you might want to investigate techniques like space partitioning.

This is a really fun topic to me. It’s rife with connections between math and computer science. There’s tons of possible extension and it can be used to create really fun projects. Most modern game engines will handle collision detection for you, but understanding how your tools work helps you get better at using them. I hope you enjoyed playing around with these examples as much as I enjoyed creating them.

All the examples in the post were written using p5.js, a javascript library meant to be accessible and used for creative expression. I made the diagrams using draw.io which will soon be changed to diagrams.net.

Build-A-Borg Workshop

This previous semester, I taught a computer programming class to a small group of students using the British Columbia curriculum. The curriculum focuses very much on process over content. In computer programming, the emphasis is on the development process over writing code. One thing I like about this is that there are lots of opportunities to get students off of their computers. While I love programming, I want the students to see it doesn’t require being glued to a screen all day.

My latest project was inspired, as many great projects are, by student interest. When trying to explain logic using light switches as an example I could see eyes glazing over. I noticed a student with a stuffed animal attached to her backpack and I asked if I could borrow it. I asked the class to imagine we were going to give this toy an upgrade and add lights and buttons to it. Maybe rubbing its belly would cause its cheeks to light up or booping its nose might make it coo. They quickly came up with many ideas for how we could use buttons lights and sounds to make this toy more exciting. After this success in a theoretical lesson, I asked if they’d be interested in making this a reality and an enthusiastic “yes” was the consensus.

That evening I gathered supplies and went out and found the cutest stuffed animal that was on sale and of reasonable size. To create our cyborg bears we also needed an Arduino with a USB cable, breadboard, some LEDS, resistors, push buttons, wires, alligator clips, and a sewing kit to open and close our toy.

During the course of this project I gave mini-lessons on boolean logic including truth tables, if-statements, how to write programs in processing and upload it to an Arduino, prototyping with breadboards and making circuits.

Students each sketched the bear and came up with ideas for where to put push buttons (inputs) and LEDs (outputs). I then had them create truth tables that described which lights would come on for each combination of buttons being pressed. This would be translated into a program in processing that would be uploaded to their Arduinos. At the same time they needed to create the necessary circuits. They could test out the functionality before doing anything with the stuffed animal.

While everyone loved the idea of enhancing the bear, they gave looks of horror when I told them it was time to open the bear up to put our circuit inside. No one else wanted to make the first cut so I did it for them but once that part was over it was no time before the were ripping excess stuffing out to make it easier to get their hand inside and connect LEDs to their wires.

It was pretty orderly at first, but as more LEDs and buttons were put in, the amount of wires caused a bit of a mess so strategizing how to best physically organize everything became important.

The first LED and button being added and working was a big milestone that got everyone excited about seeing the final result. After school, students brought their friends from other classes to come look at what they had made.

At the end we had to sew it back up but a hole was left to plug the USB cable into the Arduino for power and so we could upload code from different students to change the functionality. This was a great chance to drive home the fact that computer science is an iterative and creative process.

What went well

  • Student motivation was through the roof during this project. Their  sense of pride after each phase was extremely evident
  • There was great collaboration between students, everyone wanted to participate and the readily helped troubleshoot for one another.
  • There was some authentic struggle taking place that students persisted through due to just wanting to see it work
  • The project was real world and “messy” challenges/obstacles presented themselves like getting wires tangled up, having exposed wires touching causing circuits to fail, etc… They had to physically get the components somewhere inside the bear they wanted instead of just neat and orderly on a breadboard

What I would change

  • I’d set more explicit expectations about writing in their engineering journals regularly. I got them talking and discussing but getting them to do written reflection was tough so some of that great talk didn’t get recorded
  • I’d like to come up with a wider variety of project ideas. There’s no one-size-fits-all project that hooks everyone so the more ideas I bank up, the more likely I am to be able to get more students engaged and energized about what they’re working on.
  • Extending what I did, I would’ve like to use more types of input/output like something to make noise, moving parts, temperature sensor, gyro sensor, etc… We also could’ve made the changes to the bear more permanent by making a pouch and inserting a battery pack instead of needing to have it plugged up and soldering everything in place instead of holding it together with prototyping materials (though as is, it’s nice since we can take it all apart and have new students reuse everything)

 

Machine Learning Lesson

A very important part of my job as a K-12 computer science teacher is to take concepts that may seem impossibly complex to some and come up with ways to make them more accessible to all of my students. In this post, I’d like to share an activity I did with my students grades 7-10 to engage them in machine learning.

There are many aspects to machine learning and the process I guided my students through is predictive modelling which consists of 5 steps:

  1. Obtaining data
  2. Correctly formatting the data
  3. Training a model with the data
  4. Testing your model
  5. Improving your model

Like many processes in engineering/design, this is not a process you step through once. It is a cycle you perform multiple iterations of and there is no set number of times you should go through the cycle.

I was inspired to tackle this subject with my students after watching a video on youtube by LearningCode.academy: “Machine Learning Tutorial for Beginners – USING JAVASCRIPT!”

There were several things I liked about this video:

  • It was short (less than 12 minutes)
  • I could jump right into the provided example code and start playing around with it with no setup required
  • The problem presented was easy to understand

All these things told me I could make a lesson plan out of it. If I stand in front of my students and lecture for more than 10-15 minutes I will lose most of their attention. I need to be able to get them engaged in a meaningful task for the most learning to occur. So, having tedious setup to go through on their computers would also be detrimental which is why I like online tools like codepen for in-class activities.

I did feel like I needed a different problem from the one in the video, one that could encourage both collaboration and individual effort. I thought of a well-known beginner project in machine learning, Building a model to recognize handwritten digits in Tensorflow. That project, as it is, is a bit out of scope for a 1-hour class with middle school students so I created my own version. I like the notion of recognizing digits, there are 10 distinct items we want to train our model to recognize and there are many ways to collaborate here. Even if students are tempted to split up the individual digits and have specific people get the model working for specific digits, they’ll still have to put their data together and test it out and find test cases that fail and work together to improve the model.

Here’s the program I made. I used two libraries. First, brain.js  (Neural Networks implemented in JavaScript) and second, p5.js (a JavaScript library similar to Processing, made to make programming more accessible).

I used p5.js since it’s something we’ve used in or class many times. In this program, I used it to draw a 6×4 grid of cells that start off empty but that the user can fill by clicking on them. Whenever the user “draws” in this way, the program attempts to guess what digit was drawn. I currently only provided training data for the digits 0-3. The students had do do the rest of the work to make it work for all digits 0-9. I wanted to make the representation of this grid as simple as possible so I made a 1-dimensional array of 24 1’s and 0’s: A 1 is a cell that’s filled in and a 0 is a cell that’s empty. They go from left-to-right, bottom-to-top the same way we read. So, as a class we looked at an example like the ‘2’ that I drew above and produced an array that would represent it: [1,1,1,1,  1,0,0,1, 0,0,0,1, 1,1,1,1, 1,0,0,0, 1,1,1,1]. The whitespace between groups of 4 help us to understand that each group of 4 represents a row in our grid. The training function in the code is where we have to insert our training data. A line might look like this:

{ input: [1,1,1,1, 1,0,0,1, 0,0,0,1, 1,1,1,1, 1,0,0,0, 1,1,1,1], output: { 2: 1 } },

We input the grid, and the output says that the probability that the input we provided is a “2” is 1 (i.e. We’re 100% confident this is a “2”). By this point, the students wanted to jump in and start adding their own data.

At the beginning of the tasks many of the questions are technical (maybe they’re getting syntax errors because they forgot a bracket or a curly brace), but once every figures out how to successfully add new test cases there are process questions like “How many test cases do I need to add?” I don’t give any guidance on this front, I just let them know that we’ll be testing out their implementations together. This encourages them to test out their own model beforehand. I also ask them questions as I’m circulating the room like “How many different ways can you think of to draw a 4?” or “Are there any numbers that could look similar to one another?”

Once we move onto testing, we quickly see that no one’s program works all the time. There’s a good chance we’ll be able to easily find some cases that are obviously wrong. So I might end up with something like this:

I can see why our program might think this is an 8, if we filled in 3 or 4 cells on the left border it would definitely look like an 8. But, as humans, we can look at this and agree that it’s most likely a 3. To improve our program we can train it with this case. In our program it would like like this:

{ input: [1,1,1,1, 0,0,0,1, 0,0,0,1, 0,1,1,1, 0,0,0,1, 1,1,1,1], output: { 3: 1 } },

And, after we re-run our program and try again, it gets it right:

We had good discussion both before and after the activity. We talked about how this process is different than the ways we’ve implemented algorithms in class before, where we give the program a step-by-step procedure to follow that we can easily trace. In this task, we give the program examples and it comes up with the rules. It’s messy and not as predictable, but it’s quite powerful. Trying to write our own algorithm to take a list of 24 1’s and 0’s and determine what digit it most closely resembles would be very difficult and the edge cases would take a long time to account for. Here, if we find a case that doesn’t work, we just throw another line of training data at it.

And if we look back at those 5 steps of predictive modelling, we’ve done each one:

  • Obtaining data – coming up with different ways to represent the digits
  • Correctly formatting the data – putting them into lines of code that syntactically work in our program
  • Training a model with the data – running the program after we’ve input the new lines
  • Testing your model – trying it out, drawing digits and seeing if it gets them right
  • Improving your model – when we find failed cases, we add more training data (Which sends up back to step 1)

 

What’s next?

Teaching at the secondary level, we tend to go broad but not too deep. I expose the students to all kinds of topics in computer science but the main learning goal is to build their skills in engaging in the processes rather than remembering all the details of the content. But, every now and then, some students will really latch on to a particular topic we covered which could help inform future projects that they either complete for my class or on their own. There are lots of directions to go from this simple lesson.

Bigger Test Cases

We just looked at a 6×4 grid. The original data set I referenced, MNIST, represented hand drawn digits as 28×28 grids. Or if you want to move into image recognition, a single image could have thousands or even millions of pixels with different color data in each.

More Training Data

At the end of the activity, we might have generated dozens or depending on your class size, hundreds of test cases but in the world of Big Data, that’s miniscule. MNIST has 60,000 training cases and 10,000 for testing. And think about how much data you’d want to give to something like a self-driving car where lives are on the line. You will have to develop more sophisticated methods for collecting and using data if you want to start working with large quantities of it.

Offline Training

The program I wrote trains the model in real-time. Once you start using much larger sets of data the time it takes to train the program increases dramatically. To accommodate this, you will need to be able to train the data offline and generate a pre-trained network that you could plop into a website if you wanted people to present it without having to wait for it to re-train every time.

Resources

Between the time I ran this lesson and writing this post, A new javascript library was released by Google’s AI team: tensorflow.js. But if you really wanted to dig into machine learning, Javascript is not the best way to go due to performance. I like it for the classroom for the speed with which we can implement and test our programs. Using TensorFlow with Python would be a good route to explore.