{"cells":[{"cell_type":"markdown","metadata":{"id":"5VesYwKAD_e3"},"source":["### Object Oriented Programing (OOP)"]},{"cell_type":"markdown","metadata":{"id":"xCEMB42hmtfE"},"source":["Plan:\n","1. **Introduction**\n","2. **Core OOP concepts**  \n","   - Classes and objects\n","   - Attributes and methods\n","   - Encapsulation\n","   - Abstraction\n","   - Inheritance\n","   - Polymorphism\n","3. **OOP in deep learning (very brief overview)**\n","   - Loading datasets and forming data batches using classes\n","   - Designing and defining deep learning models using classes\n","4. **Questions to self-test your understanding of OOP concepts**\n"]},{"cell_type":"markdown","metadata":{"id":"coto-5Aqnf75"},"source":["### **1. Introduction**"]},{"cell_type":"markdown","metadata":{"id":"tyzcyz99lhSw"},"source":["Python is an object-oriented language which offers methods for creating classes and defining objects.\n","\n","A class is a collection instance variables and methods which together defines the nature or characteristics of an object type. Classes a basically the blue prints or templates from which objects are instantiated.\n","\n","An object is an instance of a class with its attributes or properties defined. A class serve as a construct for many objects"]},{"cell_type":"markdown","metadata":{"id":"NCynS6lcrvpV"},"source":["### **2. Core OOP concepts**"]},{"cell_type":"markdown","metadata":{"id":"GJn5gBoGrvpi"},"source":["**Classes and objects**"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"mD1FKFXlrvpi","outputId":"4171ad3d-32bd-445b-c24a-36e7f934193d"},"outputs":[{"output_type":"stream","name":"stdout","text":["<__main__.CMUStudent object at 0x7ee6d5eb4eb0>\n","<__main__.CMUStudent object at 0x7ee6d5eb6ec0>\n"]}],"source":["# Creating a class CMUStudent\n","class CMUStudent:\n","    def __init__(self, name, major, id): # __init__ is a constructor for defining the class variables\n","        self.name = name\n","        self.major = major\n","        self.id = id\n","\n","# Creating an object of class CMUStudent\n","student_1 = CMUStudent(\"Alice\", \"Computer Science\", \"alandy\")\n","student_2 = CMUStudent(\"Daniel\", \"Physics\", \" danielsh\")\n","\n","# Printing the students just tells us that the variables are objects of class CMUStudent with their memory locations\n","print(student_1)\n","print(student_2)"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"awcbbgtlrvpj","outputId":"c47432bd-a7a1-4cff-e448-6333161600bd"},"outputs":[{"output_type":"stream","name":"stdout","text":["student_1.name Alice\n","student_1.major Computer Science\n","student_1.id alandy\n"]}],"source":["# We can also get the individual attribute of each student using the '.' operator\n","print('student_1.name', student_1.name)\n","print('student_1.major', student_1.major)\n","print('student_1.id', student_1.id)"]},{"cell_type":"markdown","metadata":{"id":"l5qCTstervpj"},"source":["**Methods**"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"X8XKJypYrvpj"},"outputs":[],"source":["class CMUStudent:\n","    def __init__(self, name, major, id, score):\n","        self.name = name\n","        self.major = major\n","        self.id = id\n","        self.score = score\n","\n","    def introduce(self):\n","        return f\"My name is {self.name} and I study {self.major}. My Andrew id is {self.id}.\"\n","\n","     # Define some other random method for this class (getting final score which is multiplied by original score *10)\n","    def getScore(self):\n","      FinalScore = self.score*10\n","      return FinalScore\n","\n","    # We can add the __repr__ method which gives details of the object attributes\n","    def __repr__(self):\n","        return f\"Name: {self.name}, Major: {self.major}, Andrew ID: {self.id}, Score: {self.score}\""]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"WVCfTD6hLgta","outputId":"a92bed36-249f-4782-c7a3-b5285d1daea6"},"outputs":[{"output_type":"stream","name":"stdout","text":["My name is Alice and I study Computer Science. My Andrew id is alandy.\n"]}],"source":["# Creating an object and using its methods and attributes\n","student_1 = CMUStudent(\"Alice\", \"Computer Science\", \"alandy\", 5)\n","print(student_1.introduce())"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"w3U_ViBHhQqG","colab":{"base_uri":"https://localhost:8080/"},"outputId":"d4637533-d4d6-4bfc-b914-926bac2137b5"},"outputs":[{"output_type":"stream","name":"stdout","text":["Name: Alice, Major: Computer Science, Andrew ID: alandy, Score: 5\n"]}],"source":["print(student_1)"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"pJ8D-ZYLLiPF","colab":{"base_uri":"https://localhost:8080/"},"outputId":"360b042e-474b-4af7-d0e5-704cd9f2dd6d"},"outputs":[{"output_type":"stream","name":"stdout","text":["72.0\n"]}],"source":["# Creating an object and using its methods and attributes\n","student_1 = CMUStudent(\"Daniel\", \"Physics\", \"danielsh\", 7.2)\n","print(student_1.getScore())"]},{"cell_type":"markdown","metadata":{"id":"L34lkt8Frvpk"},"source":["**Encapsulation**"]},{"cell_type":"markdown","metadata":{"id":"sVUjkUbciv29"},"source":["This is a core concept or advantage of OOP. Encapsulation provides a means of preventing unauthorized access to some instance varibles of an object. This keeps the variables hidden and inaccessible often referred to as private variables\n","\n","To create a private variable we use the double underscore (__variableName) in front of the variable name"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"SuhlFU4yrvpk"},"outputs":[],"source":["class CMUStudent:\n","    def __init__(self, name, major, id, score):\n","        self.name = name\n","        self.major = major\n","        self.id = id\n","\n","        self.__score = score # We make score as a Private variable\n","\n","    def get_info(self):\n","        return f\"Name: {self.name}, Major: {self.major}, Andrew ID: {self.id}, Score: {self.__score}\"\n","\n","    def getScore(self):\n","      FinalScore = self.score*10\n","      return FinalScore\n","\n","    def __repr__(self):\n","        return f\"Name: {self.name}, Major: {self.major}, Andrew ID: {self.id}, Score: {self.score}\""]},{"cell_type":"code","execution_count":null,"metadata":{"id":"oW_rJtXFfWRW","colab":{"base_uri":"https://localhost:8080/"},"outputId":"df2e2862-8779-43cf-8591-6388059c85b8"},"outputs":[{"output_type":"stream","name":"stdout","text":["student_1.name Daniel\n","Name: Alice, Major: Computer Science, Andrew ID: alandy, Score: 6.3\n"]}],"source":["# Accessing information\n","print('student_1.name', student_1.name)\n","\n","# Accessing information through a method\n","student_1 = CMUStudent(\"Alice\", \"Computer Science\", \"alandy\", 6.3)\n","print(student_1.get_info())"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"51Ig2zbpfmYg","colab":{"base_uri":"https://localhost:8080/"},"outputId":"8e6a247a-587d-46dd-db61-ee1e0751faeb"},"outputs":[{"output_type":"stream","name":"stdout","text":["Alice\n"]}],"source":["print(student_1.name)"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"4JP7H-_MfqTT","colab":{"base_uri":"https://localhost:8080/","height":141},"outputId":"764abc14-eeea-4ec6-eb01-13002ddfef59"},"outputs":[{"output_type":"error","ename":"AttributeError","evalue":"'CMUStudent' object has no attribute '__score'","traceback":["\u001b[0;31m---------------------------------------------------------------------------\u001b[0m","\u001b[0;31mAttributeError\u001b[0m                            Traceback (most recent call last)","\u001b[0;32m<ipython-input-11-d23b3598aea7>\u001b[0m in \u001b[0;36m<cell line: 1>\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mstudent_1\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__score\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m#This doesn't work because __score is a Private variable\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m","\u001b[0;31mAttributeError\u001b[0m: 'CMUStudent' object has no attribute '__score'"]}],"source":["print(student_1.__score) #This doesn't work because __score is a Private variable"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"AiZdhadSs9_M"},"outputs":[],"source":["# We can use setters and getter methods to access these private variables\n","class CMUStudent:\n","    def __init__(self, name, major, id, score):\n","        self.name = name\n","        self.major = major\n","        self.id = id\n","\n","        self.__score = None # We make score as a Private variable\n","        # can also initialise as self.__score = score\n","\n","    # Define a setter and getter for the score (we can do this separately for any private variables)\n","    def set_score(self, score):\n","      self.__score = score\n","\n","    def get_score(self):\n","      FinalScore = self.__score*10\n","      return FinalScore\n","\n","    def __repr__(self):\n","        return f\"Name: {self.name}, Major: {self.major}, Andrew ID: {self.id}, Score: {self.score}\""]},{"cell_type":"code","execution_count":null,"metadata":{"id":"jYrjPygWtus7","colab":{"base_uri":"https://localhost:8080/"},"outputId":"6e8b8963-40ae-4669-f44b-1ed492be1eb6"},"outputs":[{"output_type":"stream","name":"stdout","text":["None\n"]}],"source":["student_1 = CMUStudent(\"Alice\", \"Computer Science\", \"alandy\", 6.3)\n","print(student_1.set_score(5.5)) # Doesn't print anything because we are seting the score (no return)"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"sHT6qd5yt05X","colab":{"base_uri":"https://localhost:8080/"},"outputId":"1cc6feb7-7684-4fe0-d4b6-1bb64a4f35e3"},"outputs":[{"output_type":"stream","name":"stdout","text":["55.0\n"]}],"source":["print(student_1.get_score())"]},{"cell_type":"markdown","metadata":{"id":"oQkQa3XJrvpl"},"source":["**Abstraction**"]},{"cell_type":"markdown","metadata":{"id":"eDQcuO1IwBl_"},"source":["The next concept is abstraction.\n","Abstraction in object-oriented programming is about simplifying complex systems by modeling classes based on the essential properties and behaviors relevant to the program while hiding unnecessary details.\n","\n","\n","\n","\n"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"V_li2Agyv3PR","colab":{"base_uri":"https://localhost:8080/"},"outputId":"216b97c7-fcd8-4e04-c7b6-d690f549ae52"},"outputs":[{"output_type":"stream","name":"stdout","text":["Name: Alice, Major: Computer Science, Andrew ID: alandy\n","GPA: 89.0\n"]}],"source":["class CMUStudent:\n","    def __init__(self, name, major, id):\n","        self.name = name\n","        self.major = major\n","        self.id = id\n","        self.__grades = []  # Private variable to store grades\n","\n","    def add_grade(self, grade):\n","        self.__grades.append(grade)\n","\n","    def calculate_gpa(self):\n","        if not self.__grades:\n","            return \"GPA: N/A\"\n","        average_grade = sum(self.__grades) / len(self.__grades)\n","        return f\"GPA: {average_grade}\"\n","\n","    def display_info(self):\n","        print(f\"Name: {self.name}, Major: {self.major}, Andrew ID: {self.id}\")\n","\n","# Example usage\n","student = CMUStudent(\"Alice\", \"Computer Science\", \"alandy\")\n","\n","# Abstraction in action\n","student.add_grade(90)\n","student.add_grade(85)\n","student.add_grade(92)\n","\n","student.display_info()\n","print(student.calculate_gpa())"]},{"cell_type":"markdown","metadata":{"id":"FGZBk1B1rvpl"},"source":["**Inheritance**"]},{"cell_type":"markdown","metadata":{"id":"GAY-iA5FvFBR"},"source":["This is another concept of OOP which allows new classes to be created off from other classes. Inheritance simply allows new classes to inherit other classes templates/blueprints (i.e. methods and variables) in creating theirs.\n","\n","The subclass or child class is the class that inherits. The superclass or parent class is the class from which methods and/or attributes are inherited."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"i7_djOAHrvpl","colab":{"base_uri":"https://localhost:8080/"},"outputId":"43ab2ddc-d642-43f5-db6e-5301654f8198"},"outputs":[{"output_type":"stream","name":"stdout","text":["Name: Bob, Major: Electrical Engineering, Andrew ID: boblee\n","Research Area: Signal Processing\n","GPA: 91.5\n"]}],"source":["class GraduateStudent(CMUStudent):\n","    def __init__(self, name, major, id, research_area):\n","        super().__init__(name, major, id)\n","        self.research_area = research_area\n","\n","    def display_info(self):\n","        super().display_info()\n","        print(f\"Research Area: {self.research_area}\")\n","\n","# Example usage\n","grad_student = GraduateStudent(\"Bob\", \"Electrical Engineering\", \"boblee\", \"Signal Processing\")\n","\n","# Inherited and extended functionality\n","grad_student.add_grade(95)\n","grad_student.add_grade(88)\n","\n","grad_student.display_info()\n","print(grad_student.calculate_gpa())"]},{"cell_type":"markdown","metadata":{"id":"NttrQoaLrvpl"},"source":["**Polymorphism**"]},{"cell_type":"markdown","metadata":{"id":"ikHkk-dc-MJ4"},"source":[" This is the ability of a subclass to change a method which already exists in the parent class to meet its own needs.\n","\n","\n"," Note: Python does not support method overloading i.e., you can not use same name for different methods in a class even if their return type and/or number of arguments are different as you would do in other programming languages"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"go1SkBZXrvpl","colab":{"base_uri":"https://localhost:8080/"},"outputId":"1892c267-1d89-4ac4-9aa3-3c3e845f1610"},"outputs":[{"output_type":"stream","name":"stdout","text":["Studying Computer Science at CMU\n","Studying Engineering at MIT\n"]}],"source":["class Student:\n","    def study(self):\n","        pass\n","\n","class CMUStudent(Student):\n","    def study(self):\n","        return \"Studying Computer Science at CMU\"\n","\n","class MITStudent(Student):\n","    def study(self):\n","        return \"Studying Engineering at MIT\"\n","\n","# Polymorphic behavior\n","students = [CMUStudent(), MITStudent()]\n","for student in students:\n","    print(student.study())"]},{"cell_type":"markdown","metadata":{"id":"Zmo7_nf_nnYm"},"source":["### **3. OOP in deep learning**"]},{"cell_type":"markdown","metadata":{"id":"cKcKGGsvvMOx"},"source":["**Why do we recommend using OOP in deep learning tasks?**\n","\n","* Enhances code reusability across projects.\n","* Keeps code clean and reduces overlap.\n","* Enables easy modifications and additions to models.\n","\n","**When to use OOP in deep learning?**\n","\n","You can essentially use it at every stage of the model pipeline:\n","\n","* Loading datasets and forming data batches --> a **`Dataloader`** class.\n","* Designing and defining deep learning models --> a **`Model`** class\n","* (Optional) Organizing and managing model training, testing, and evaluation processes --> **`Train`**, **`Evaluation`**, **`Test`** classes.\n","\n","Let's see some toy examples."]},{"cell_type":"markdown","metadata":{"id":"F4dRlh2JuNtR"},"source":["**Loading datasets and forming data batches using classes**"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"JxDT79d_uZY2","colab":{"base_uri":"https://localhost:8080/"},"outputId":"1f0bbc22-6f01-43cb-f065-2f3c2da9f5a9"},"outputs":[{"output_type":"stream","name":"stdout","text":["Full dataset [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]\n","Batch 0 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\n","Batch 1 [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]\n"]}],"source":["# Simple dataloader\n","\n","class SimpleDataLoader:\n","    # init constructor\n","    def __init__(self, dataset, batch_size):\n","        self.dataset = dataset\n","        self.batch_size = batch_size\n","    # our custom get batches method\n","    def get_batches(self):\n","        for i in range(0, len(self.dataset), self.batch_size):\n","            yield self.dataset[i:i + self.batch_size]\n","\n","# Example usage\n","\n","# Create a dummy dataset - a list of numbers in this case.\n","dataset = [i for i in range(1, 21)]\n","print('Full dataset', dataset)\n","\n","# Instantiate the DataLoader with the dataset and a batch size\n","loader = SimpleDataLoader(dataset,\n","                          batch_size=10)\n","\n","# Iterate over the DataLoader to get and print each batch\n","for i, batch in enumerate(loader.get_batches()):\n","    print('Batch', i, batch)"]},{"cell_type":"markdown","metadata":{"id":"rI_vvxHIuUn9"},"source":["**Designing and defining deep learning models using classes**"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"sreQccC1upMP","colab":{"base_uri":"https://localhost:8080/"},"outputId":"5b4da663-0e32-4c79-cda7-2016c7bd8e8c"},"outputs":[{"output_type":"stream","name":"stdout","text":["Output: 11\n"]}],"source":["# Simple model - version 1\n","\n","class SimpleModel:\n","\n","    def __init__(self):\n","\n","        # Define the transformations\n","        self.transformation_1  = lambda x: x * 2 # doubling the input\n","        self.transformation_2  = lambda x: x + 2 # adding 2\n","        self.transformation_3  = lambda x: x // 2 # halving the output\n","\n","    def forward(self, initial_data):\n","\n","        # Call the transformations\n","        transformed_data = self.transformation_1(initial_data)\n","        transformed_data = self.transformation_2(transformed_data)\n","        transformed_data = self.transformation_3(transformed_data)\n","\n","        return transformed_data  # Return the transformed data\n","\n","# Example usage\n","\n","# Initialize the model\n","model = SimpleModel()\n","\n","# Call the model\n","input_data = 10\n","print(\"Output:\", model.forward(input_data))"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"hgT9jPdDuL2B","colab":{"base_uri":"https://localhost:8080/"},"outputId":"e43e9338-0268-491b-bb82-9ade7da3ceaf"},"outputs":[{"output_type":"stream","name":"stdout","text":["Output: 11\n"]}],"source":["# Simple model - version 2 (with additional parameters)\n","\n","class SimpleModel:\n","\n","    def __init__(self, parameter_multiply, parameter_add, parameter_divide):\n","\n","        # Define the transformations\n","        self.transformation_1  = lambda x: x * parameter_multiply\n","        self.transformation_2 = lambda x: x + parameter_add\n","        self.transformation_3 = lambda x: x // parameter_divide\n","\n","    def forward(self, initial_data):\n","\n","        # Call the transformations\n","        transformed_data = self.transformation_1(initial_data)\n","        transformed_data = self.transformation_2(transformed_data)\n","        transformed_data = self.transformation_3(transformed_data)\n","\n","        return transformed_data # Return the transformed data\n","\n","# Example usage\n","\n","# Initialize the model\n","model = SimpleModel(parameter_multiply = 2,\n","                    parameter_add      = 2,\n","                    parameter_divide   = 2)\n","\n","# Call the model\n","input_data = 10\n","print(\"Output:\", model.forward(input_data))"]},{"cell_type":"markdown","metadata":{"id":"5LMOSA2cnzpG"},"source":["### **4. Questions to self-test your understanding of OOP concepts**"]},{"cell_type":"markdown","metadata":{"id":"HvkWKcAHZN2a"},"source":["**Q1: How can OOP make the process of preparing data for a neural network more efficient?**\n","\n","Hint: Reflect on how classes can streamline the steps involved in preparing data.\n","\n","**Q2: How do classes make it easier to understand and manage a deep learning model with many layers?**\n","\n","Hint: Think about how dividing the code into classes can help manage larger, more complex projects.\n","\n","**Q3: Why is OOP beneficial for teamwork in deep learning projects?**\n","\n","Hint: Focus on the role of OOP in making the code more readable and organized. Also, consider how features like inheritance in OOP allow for easy modifications."]},{"cell_type":"markdown","metadata":{"id":"nj2rLhZ1aLru"},"source":["Thanks for reviewing this material!"]}],"metadata":{"colab":{"collapsed_sections":["NCynS6lcrvpV","Zmo7_nf_nnYm"],"provenance":[]},"kernelspec":{"display_name":"Python 3","name":"python3"},"language_info":{"name":"python"}},"nbformat":4,"nbformat_minor":0}