Spaces:
Runtime error
Runtime error
update A bite of math
Browse filesAdding pages and change the password function
- __pycache__/utils.cpython-310.pyc +0 -0
- app.py +13 -2
- pages/11_A_Bite_Of_Maths.py +16 -0
- pages/2_201_Text_summarisation_with_LLM.py +1 -1
- pages/2_202_Access_to_HuggingFace_with_Notebook.py +12 -33
- pages/3_301_Running_streamlit_locally.py +1 -1
- pages/3_302_Our_First_App.py +1 -1
- pages/pages/1_Learning_from_scratch.py +182 -0
- pages/pages/2_Learning_graduately.py +143 -0
- requirements.txt +2 -1
- tmp/users.csv +10 -0
- utils.py +5 -5
__pycache__/utils.cpython-310.pyc
ADDED
Binary file (837 Bytes). View file
|
|
app.py
CHANGED
@@ -1,14 +1,23 @@
|
|
1 |
import streamlit as st
|
|
|
|
|
|
|
|
|
2 |
|
3 |
st.set_page_config(
|
4 |
page_title="Welcome to AI club",
|
5 |
page_icon="👋",
|
6 |
)
|
7 |
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
## Page layout
|
9 |
@st.cache_data
|
10 |
def main_layout():
|
11 |
-
#st.sidebar.success("Select weekly task above.")
|
12 |
st.markdown(
|
13 |
"""
|
14 |
This is our weekly tutorial pages.
|
@@ -30,4 +39,6 @@ st.markdown("""
|
|
30 |
- [Hugging Face HowTos](https://huggingface.co/docs/hub/spaces)
|
31 |
- [Learn to ClickUp](https://help.clickup.com/hc/en-us/categories/5414365970455-Features)
|
32 |
"""
|
33 |
-
)
|
|
|
|
|
|
1 |
import streamlit as st
|
2 |
+
import extra_streamlit_components as stx
|
3 |
+
import spacy_streamlit
|
4 |
+
import streamlit_book as stb
|
5 |
+
|
6 |
|
7 |
st.set_page_config(
|
8 |
page_title="Welcome to AI club",
|
9 |
page_icon="👋",
|
10 |
)
|
11 |
|
12 |
+
def get_manager():
|
13 |
+
return stx.CookieManager()
|
14 |
+
|
15 |
+
cookie_manager = get_manager()
|
16 |
+
|
17 |
+
|
18 |
## Page layout
|
19 |
@st.cache_data
|
20 |
def main_layout():
|
|
|
21 |
st.markdown(
|
22 |
"""
|
23 |
This is our weekly tutorial pages.
|
|
|
39 |
- [Hugging Face HowTos](https://huggingface.co/docs/hub/spaces)
|
40 |
- [Learn to ClickUp](https://help.clickup.com/hc/en-us/categories/5414365970455-Features)
|
41 |
"""
|
42 |
+
)
|
43 |
+
|
44 |
+
|
pages/11_A_Bite_Of_Maths.py
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
## Just A bit of Maths
|
2 |
+
import streamlit as st
|
3 |
+
import streamlit_book as stb
|
4 |
+
|
5 |
+
stb.set_book_config(
|
6 |
+
menu_title="A bite of Maths",
|
7 |
+
menu_icon="lightbulb",
|
8 |
+
options=[
|
9 |
+
"CH1: Learn from scratch",
|
10 |
+
"CH2: Learn graduately"
|
11 |
+
],
|
12 |
+
paths=[
|
13 |
+
"pages/pages/1_Learning_from_scratch.py",
|
14 |
+
"pages/pages/2_Learning_graduately.py"
|
15 |
+
],
|
16 |
+
)
|
pages/2_201_Text_summarisation_with_LLM.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
import streamlit as st
|
2 |
from utils import *
|
3 |
|
4 |
-
if check_password():
|
5 |
st.markdown("""
|
6 |
## With ChatGPT
|
7 |
|
|
|
1 |
import streamlit as st
|
2 |
from utils import *
|
3 |
|
4 |
+
if check_password("password"):
|
5 |
st.markdown("""
|
6 |
## With ChatGPT
|
7 |
|
pages/2_202_Access_to_HuggingFace_with_Notebook.py
CHANGED
@@ -1,36 +1,7 @@
|
|
1 |
import streamlit as st
|
2 |
from utils import *
|
3 |
|
4 |
-
|
5 |
-
"""Returns `True` if the user had the correct password."""
|
6 |
-
|
7 |
-
def password_entered():
|
8 |
-
"""Checks whether a password entered by the user is correct."""
|
9 |
-
if st.session_state["password"] == st.secrets["password"]:
|
10 |
-
st.session_state["password_correct"] = True
|
11 |
-
del st.session_state["password"] # don't store password
|
12 |
-
else:
|
13 |
-
st.session_state["password_correct"] = False
|
14 |
-
|
15 |
-
if "password_correct" not in st.session_state:
|
16 |
-
# First run, show input for password.
|
17 |
-
st.text_input(
|
18 |
-
"Password", type="password", on_change=password_entered, key="password"
|
19 |
-
)
|
20 |
-
return False
|
21 |
-
elif not st.session_state["password_correct"]:
|
22 |
-
# Password not correct, show input + error.
|
23 |
-
st.text_input(
|
24 |
-
"Password", type="password", on_change=password_entered, key="password"
|
25 |
-
)
|
26 |
-
st.error("😕 Password incorrect")
|
27 |
-
return False
|
28 |
-
else:
|
29 |
-
# Password correct.
|
30 |
-
return True
|
31 |
-
|
32 |
-
|
33 |
-
if check_password():
|
34 |
st.markdown("""
|
35 |
## Notebook to Hugging Face Space
|
36 |
|
@@ -95,8 +66,16 @@ if check_password():
|
|
95 |
##### 5. Update your changes to the specific branch
|
96 |
|
97 |
1. If you have edited the app.py file with your profile, the change already happened in your local code folder;
|
98 |
-
2. For simplicity, you will upload the updated folder "Tutorials" to the "
|
99 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
100 |
|
101 |
Using the code snippet below to update your changes,
|
102 |
- You will need to do detailed editing and check before you submit it, otherwise, your pull request would not be accepted
|
@@ -105,7 +84,7 @@ if check_password():
|
|
105 |
api.upload_folder(folder_path="./Tutorials", # the folder path at the local machine
|
106 |
repo_id="TGSAI/Tutorials",
|
107 |
repo_type="space",
|
108 |
-
revision = '
|
109 |
commit_message = 'https://app.clickup.com/t/860r79ppd', # add comment, mostly link to Clickup ticket url
|
110 |
commit_description = '', # any discription, or omitted
|
111 |
create_pr = True # Important! You must create Pull Request, then the maintainer will need to check it.
|
|
|
1 |
import streamlit as st
|
2 |
from utils import *
|
3 |
|
4 |
+
if check_password("password"):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
st.markdown("""
|
6 |
## Notebook to Hugging Face Space
|
7 |
|
|
|
66 |
##### 5. Update your changes to the specific branch
|
67 |
|
68 |
1. If you have edited the app.py file with your profile, the change already happened in your local code folder;
|
69 |
+
2. For simplicity, you will upload the updated folder "Tutorials" to the "main" branch;
|
70 |
+
|
71 |
+
2.5. But before you commit change, you should **always** update your local code with newest change at our code space, by:
|
72 |
+
```python
|
73 |
+
from huggingface_hub import Repository
|
74 |
+
repo = Repository(local_dir="<Your local folder>", clone_from="<The HF folder>", revision='main')
|
75 |
+
repo.git_pull(rebase=True) # This will update your local folder
|
76 |
+
```
|
77 |
+
|
78 |
+
3. We will check your commit and merge it mannualy from HF web interface
|
79 |
|
80 |
Using the code snippet below to update your changes,
|
81 |
- You will need to do detailed editing and check before you submit it, otherwise, your pull request would not be accepted
|
|
|
84 |
api.upload_folder(folder_path="./Tutorials", # the folder path at the local machine
|
85 |
repo_id="TGSAI/Tutorials",
|
86 |
repo_type="space",
|
87 |
+
revision = 'main', # make sure upload to the main branch
|
88 |
commit_message = 'https://app.clickup.com/t/860r79ppd', # add comment, mostly link to Clickup ticket url
|
89 |
commit_description = '', # any discription, or omitted
|
90 |
create_pr = True # Important! You must create Pull Request, then the maintainer will need to check it.
|
pages/3_301_Running_streamlit_locally.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
import streamlit as st
|
2 |
from utils import *
|
3 |
|
4 |
-
if check_password():
|
5 |
st.markdown("""
|
6 |
## Set up local streamlit
|
7 |
|
|
|
1 |
import streamlit as st
|
2 |
from utils import *
|
3 |
|
4 |
+
if check_password("password"):
|
5 |
st.markdown("""
|
6 |
## Set up local streamlit
|
7 |
|
pages/3_302_Our_First_App.py
CHANGED
@@ -2,7 +2,7 @@ import streamlit as st
|
|
2 |
from utils import *
|
3 |
st.set_page_config(page_title="The first App", page_icon=":tangerine:")
|
4 |
|
5 |
-
if check_password():
|
6 |
st.markdown("## Welcome to the first app: summarizer")
|
7 |
st.write("##")
|
8 |
|
|
|
2 |
from utils import *
|
3 |
st.set_page_config(page_title="The first App", page_icon=":tangerine:")
|
4 |
|
5 |
+
if check_password("password"):
|
6 |
st.markdown("## Welcome to the first app: summarizer")
|
7 |
st.write("##")
|
8 |
|
pages/pages/1_Learning_from_scratch.py
ADDED
@@ -0,0 +1,182 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
## Maths -- 1_learning from scratch.py
|
2 |
+
|
3 |
+
import streamlit as st
|
4 |
+
|
5 |
+
|
6 |
+
st.markdown("""
|
7 |
+
#### **Chapter 1. Predict from scratch, who cares about mistakes :smiling_imp:**
|
8 |
+
|
9 |
+
##### 1.1 We will start from an simple prediction task,
|
10 |
+
|
11 |
+
> **Example forever**
|
12 |
+
>
|
13 |
+
> You are going to predict the price of a house from a selection of the following factors:
|
14 |
+
> - Living areas (on $m^2$)
|
15 |
+
> - Number of bedrooms
|
16 |
+
> - Number of garages
|
17 |
+
> - Number of bathsroom
|
18 |
+
> - nabourhood average income?
|
19 |
+
> - some other factors
|
20 |
+
|
21 |
+
The factors above are just a simplified example of all posible factors when people considering or predicting house prices. Different people might have different choices. Some people might only have one factor, such as, whether it is "good-looking" or not; where as others might have very complicated model that considering a lot of factors. **But for now**, we just use two factors that I believe it will be correct for predicting house price, and you will have nowhere to argue about this with me, just take it. The two factors are `"Living areas"` and `"Number of bedrooms"`, let's go!
|
22 |
+
""")
|
23 |
+
|
24 |
+
st.markdown("""
|
25 |
+
> **1.2 My stupid prediction initially**
|
26 |
+
|
27 |
+
> I am extremely stupid and has no idea what to do with this prediction, so I just grab whatever numbers in my head. I believe that the price per living area should be $ 100\ per\ m^2$, and the price per number of bedroom should be $ 5000\ per\ badroom$ So I made my prediction to an Algebraic equation:
|
28 |
+
""")
|
29 |
+
st.latex(r'''
|
30 |
+
Price = area \times 100\ + NumberOfBedroom \times 5000
|
31 |
+
''')
|
32 |
+
|
33 |
+
st.markdown("""
|
34 |
+
So based on my assumption, a 3 bedrooms house with $120 m^2$ of living area would cost: """)
|
35 |
+
st.latex(r'''
|
36 |
+
120 \times 100 + 3 \times 5000 = 27000
|
37 |
+
''')
|
38 |
+
|
39 |
+
st.markdown("""
|
40 |
+
Of course, my model is far from being accurate. But how can I **learn** to make it more accurate? I will learn it by collecting some information of houses sold like the table below:
|
41 |
+
|
42 |
+
| # Bedrooms | Living area | Sold Price |
|
43 |
+
| -----------| ----------- | ---------- |
|
44 |
+
| 4 | 170 | 1,200,000 |
|
45 |
+
| 3 | 130 | 980,000 |
|
46 |
+
| 5 | 230 | 1,600,000 |
|
47 |
+
| ...... | ...... | ...... |
|
48 |
+
|
49 |
+
""")
|
50 |
+
|
51 |
+
st.markdown("""
|
52 |
+
Then, I will formally "define" what I have done so far. The two factors I choosed, I will call them **variables**. The number from my stupid guess, which means, $5000$ and $100$, I will call them **weights** of each variable. So for example, 5000 is the weight of number of bedrooms, 100 is the weight of living area. Thus my model can be defined as:""")
|
53 |
+
st.latex(r'''a \times weight_a + b \times weight_b = prediction''')
|
54 |
+
st.write("in which:")
|
55 |
+
st.latex(r'''a => Living\ areas; \ b=>Number\ of\ bedrooms ''')
|
56 |
+
|
57 |
+
st.markdown("""
|
58 |
+
One more thing I will do is to add my initial predictions to the table above. Let's see how stupid I was!
|
59 |
+
|
60 |
+
|
61 |
+
| # Bedrooms | Living area | Sold Price | Myprediction|
|
62 |
+
| ---------- | ----------- | ---------- | --- |
|
63 |
+
| 4 | 170 | 1,200,000 |37000|
|
64 |
+
| 3 | 130 | 980,000 |28000|
|
65 |
+
| 5 | 230 | 1,600,000 |48000|
|
66 |
+
| ...... | ...... | ...... | |
|
67 |
+
""")
|
68 |
+
|
69 |
+
st.write("##")
|
70 |
+
st.markdown("""
|
71 |
+
##### 1.2 Loss Functions: evaluate how bad my model is,
|
72 |
+
|
73 |
+
Let's summarise what I have done so far. I creatd a function, for which I passed two sets of values to it:
|
74 |
+
|
75 |
+
- The first set of values is the value pair (Number of Bedroom, Living Area), Lets call this paired value *samples*, and write it in the form of $\Bbb{x} := (x_1, x_2)$, where $x_1$ is the Number of bedrooms and $x_2$ is the living area.
|
76 |
+
- The second set of values is the value pair (Weight1, Weight2), which is the weight for number of bedrooms and living area respectively. Let's call them just weights.
|
77 |
+
|
78 |
+
So my funciton is defined by, every time I give it two pairs, one pair of samples and one pair of weights, it give me the predicted house price. Writen in a math form:
|
79 |
+
""")
|
80 |
+
st.latex(r'''f(\Bbb{x}, \Bbb{w}) := x_1\times w_1 + x_2\times w_2''')
|
81 |
+
|
82 |
+
st.markdown("""
|
83 |
+
Now I have collected 100 data points of houses, and I have done my naive prediction by passing the pair of samples 100 times to my model (my naive **Weights** doesn't change at this time), and I got 100 **wrong** predictions. Before I could improve myself, I need to know how bad I have just done in general. This can be done easily through *Calculating the average difference between sold-price and my prediction*, the larger the average difference (Let's call it **:red[Error]** from now on), the worse my model.
|
84 |
+
|
85 |
+
But wait a munite, if I have two predictions, one is 10000 *above* the sold-price and another is 10000 *below* the sold-price, then the average would be Zero! I need a more reasonable feedback! We can handle this by squaring the errors:""")
|
86 |
+
st.latex(r'''\frac{10000^2 + (-10000)^2}{2} = 100M''')
|
87 |
+
|
88 |
+
st.markdown("""
|
89 |
+
Now, we can define this "Averaging the squaring of the errors" as a function as well! Let's call it **Loss function**, denote by $L(f(\Bbb{x}, \Bbb{w}),\Bbb{y})$, where y is the sold-prices. A warm reminder for some reader is that you can understand $L(f(\Bbb{x}, \Bbb{w}),\Bbb{y})$ as $L(prediction, y)$ for simplicity, as $f(\Bbb{x}, \Bbb{w}) = prediction$.
|
90 |
+
|
91 |
+
Our purpose of learning, or model training, is to make our Loss smaller by updating weights, in a mathematical way of expression:
|
92 |
+
""")
|
93 |
+
st.latex(r'''\arg min_{\Bbb{w}\in \Bbb{R}^d}\ \ \ \frac{1}{l}\sum_{i=1}^{l}L(f(\Bbb{x_i}, \Bbb{w}),\Bbb{y})''')
|
94 |
+
st.markdown("""
|
95 |
+
Where $\Bbb{R}^d$ is the set of all possible pairs of weights, and d is the dimension of the weights, in our example, we call them pairs, it means $d = 2$, of cause there will be high dimensional weights, once our considered factors become more and more! $l$ is the number of samples (or the number of rows in the table above), just like the 100 in our example above.
|
96 |
+
""")
|
97 |
+
|
98 |
+
st.write("##")
|
99 |
+
st.markdown("""
|
100 |
+
##### 1.3 Cool things about the loss function,
|
101 |
+
|
102 |
+
One good thing about the loss function is "**convex**". Mathematically, convexity of a function: $f: \Bbb{R}^d \mapsto \Bbb{R}$ can be express as:
|
103 |
+
""")
|
104 |
+
st.latex(r'''f(tx + (1-t)y)\le tf(x) + (1-t)f(y);\ \forall x,y \in \Bbb{R}^d,\ t\in [0,1]''')
|
105 |
+
st.markdown("""
|
106 |
+
Let's forget about the symbols and look at a graph:""")
|
107 |
+
|
108 |
+
st.image("https://i.ibb.co/KmzvqCz/Quodratic1.png", width=300,)
|
109 |
+
st.markdown("""
|
110 |
+
We will explain that Math expression with words and graph:
|
111 |
+
|
112 |
+
- From words: Picking any two points for inputs, the outputs of these two points from the function can be connected by a straight line, so curve of the function between these two points will always below or match this straight line.
|
113 |
+
|
114 |
+
- From Graph:
|
115 |
+
""")
|
116 |
+
st.image("https://i.ibb.co/D5LzPRT/Quodratic2.png", width=300,)
|
117 |
+
st.markdown("""
|
118 |
+
On the graph above, I randomly picked two points on the x-axis to generate two points on the curve, called A and B, connected them with a straight line, then all the function curve between the the range of two points on the x-axis would below or exactly match the line between A and B.This is the idea of convexity of a funciton. This helps us to answer the first question above, yes, the **:red[LOSS]** function does have a minimum (more specifically, due to convexity, a global minimum) for us to approach! Now, our second question:
|
119 |
+
|
120 |
+
> How do we approach there?
|
121 |
+
|
122 |
+
In general,we need to two pieces of information to achieve this, the direction of moving, and the magnitute of moving. Following the Graph above, we can demonstrate the idea through another graph:
|
123 |
+
""")
|
124 |
+
st.image("https://i.ibb.co/Jkkg1zz/Quodratic3.png", width=300,)
|
125 |
+
st.markdown("""
|
126 |
+
- Explain the graph:
|
127 |
+
|
128 |
+
Let's say that our LOSS of the naive model is at D. We want our loss goes downs to the bottom of the curve, one way is to go to the direction as the black arrow showed.
|
129 |
+
|
130 |
+
This can be done by letting the target variable (i.e. the horizontal one, the input of the function) *goes to the negtive direction of the **[Gradient](https://thirdspacelearning.com/gcse-maths/algebra/gradient-of-a-line/)** of the **:red[LOSS]** function*, also attached with some magnitute of moving (i.e. controlled by the term, "learning rate").
|
131 |
+
|
132 |
+
After this, our new input of the **:red[LOSS]** function would become $A^{'}$, our new output of the **:red[LOSS]** function would become E, which is lower than D, as required.
|
133 |
+
|
134 |
+
Now we know that:
|
135 |
+
|
136 |
+
- there is a minimum of our LOSS function to approach
|
137 |
+
- there is a way, we can approach the minumum
|
138 |
+
|
139 |
+
But before we can moving toward implement our method, two more question I need to consider:
|
140 |
+
|
141 |
+
- How can I be garanteed that, using the way provided here, I will eventually reach the minimum by finite number of steps?
|
142 |
+
- What is the "learning rate"?
|
143 |
+
|
144 |
+
We will answer the two questions together in the next section.
|
145 |
+
|
146 |
+
""")
|
147 |
+
|
148 |
+
with st.expander("Pass the quiz to get to the next section :)"):
|
149 |
+
|
150 |
+
st.markdown("To access to the next section, you have to finish the little test below: :smile:")
|
151 |
+
st.write("##")
|
152 |
+
st.markdown("Q1. If I have a 3-Bedrooms House with 140 $m^2$ of living area. If my weights of house price for the two variables is (1000, 2000) respectively. What would be my predicted house price based on the weights?")
|
153 |
+
|
154 |
+
q1 = st.radio("", ["A. 1.2 million", "B. 300k", "C. 283k", "D. 310k"])
|
155 |
+
|
156 |
+
st.write("##")
|
157 |
+
st.markdown("Q2. Following the Q1's context, if the true selling price for the house is 1.05 million, what is the error of my prediction?")
|
158 |
+
|
159 |
+
q2 = st.radio("", ["A. 1 million", "B. 750k", "C. 280k", "D. 767k"])
|
160 |
+
|
161 |
+
st.write("##")
|
162 |
+
st.markdown("Q3. For the function $ y = 2x^2 $, what is the gradient at $ x = -2 $?")
|
163 |
+
|
164 |
+
q3 = st.radio("", ["A. 8", "B. -8", "C. 4", "D. -4"])
|
165 |
+
|
166 |
+
st.write("##")
|
167 |
+
st.markdown("Q4. Following the Q3's context, if the learning rate is 0.0001, where my x should move to based on the Gradient Descent Algorithm?")
|
168 |
+
|
169 |
+
q4 = st.radio("", ["A. -1.9992", "B. 1.9992", "C. 0.0008", "D. -0.0008"])
|
170 |
+
|
171 |
+
st.write("##")
|
172 |
+
st.markdown("Q5. Is the linear function $ y = 3x - 8$ convex? ")
|
173 |
+
|
174 |
+
q5 = st.radio("", ["A. Yes", "B. No"])
|
175 |
+
|
176 |
+
if st.button("Try your luck..."):
|
177 |
+
if q1 == "C. 283k" and q2 == "D. 767k" and q3 == "B. -8" and q4 == "A. -1.9992" and q5 == "A. Yes":
|
178 |
+
st.write("Congratulations! The password for the next section is convexity. ")
|
179 |
+
else:
|
180 |
+
st.write("Oop! Unlucky... You may try again!")
|
181 |
+
|
182 |
+
|
pages/pages/2_Learning_graduately.py
ADDED
@@ -0,0 +1,143 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
from utils import *
|
3 |
+
|
4 |
+
if check_password("m2"):
|
5 |
+
st.markdown("""
|
6 |
+
#### **Chapter 2. By learning graduately, nothing can stop us :mechanical_arm:**
|
7 |
+
|
8 |
+
\n
|
9 |
+
\n
|
10 |
+
|
11 |
+
##### 2.1 Firstly, we will summerise what we have done so far,
|
12 |
+
|
13 |
+
- Samples of houses with factors as pair, i.e. (Number of bedrooms, living area)
|
14 |
+
- Corresponding samples of sold-price
|
15 |
+
- A pair of **NAIVE** weights correspond to the two factors
|
16 |
+
- A list of predicted price based on factors and our **NAIVE** weights
|
17 |
+
- A function that compute the **:red[LOSS]** (differences between the real sold-prices and predicted prices), then average out the list of samples.
|
18 |
+
- A way of decrease loss by updating weights: go to the negtive direction of the gradient of the loss function
|
19 |
+
|
20 |
+
We hope to learn something from the **:red[LOSS]** and thus update our weights to be a smarter pair which give us better prediction closing to the real price.
|
21 |
+
|
22 |
+
Let's describe the **:red[LOSS]** function with slightly more details. We will represent our predictions as $f(\Bbb{x}, \Bbb{w}) := \hat{y}$, called "y-hat", then the loss function can be represented as
|
23 |
+
""")
|
24 |
+
st.latex(r'''L(f(\Bbb{x_i}, \Bbb{w}),\Bbb{y})= L(\hat{y},y) :=\frac{\sum_{i=1}^{l}(\hat{y}_i-y)^2}{l}''')
|
25 |
+
st.markdown("""
|
26 |
+
As there is only one variable in the expression, we can replace $\hat{y}_i - y$ as $t_i$, so we have""")
|
27 |
+
st.latex(r'''L(t) := \frac{\sum_{i=1}^{l}t_i^2}{l}''')
|
28 |
+
st.markdown("""
|
29 |
+
It is more familiar to us. Looks like $f(t) = at^2$ for $a$ to be a positive constant, right?
|
30 |
+
|
31 |
+
We discussed that this loss function is **Convex**
|
32 |
+
""")
|
33 |
+
|
34 |
+
st.write("##")
|
35 |
+
|
36 |
+
st.markdown("""
|
37 |
+
|
38 |
+
##### 2.2 Can we reach the minimum?
|
39 |
+
|
40 |
+
However, even we know there is a minumum, and we know a way to approach the minimum, **It is not garanteed that we can eventually reach there with finite steps**, let's see a classic counterexample:
|
41 |
+
""")
|
42 |
+
st.image("https://i.ibb.co/pfJB1Q5/Quodratic4.png", width=300,)
|
43 |
+
st.markdown("""
|
44 |
+
From above, the funciton $f(x):= |x|$ is convex. If we use the method we discussed, after certain steps, the loss will stuck at a certain level without decreasing any more. So based on method of gradient descent, we need another feature from our **:red[Loss]** function, differenciability and smoothness (**NOTE**: in practice, there are ways to deal with non-differenciable loss functions such as subgradient, which is beyone the discussion here).
|
45 |
+
|
46 |
+
Luckily, our choice of “Averaging the squaring of the errors” satisfy both requirement! It is differenciable, and it is smooth, more presicely, **Lipschitz smooth**, or **L-smooth**. Definition of L-smooth can be expressed as:
|
47 |
+
|
48 |
+
- A continuously differentiable function is L-smooth if its gradient satisfy:
|
49 |
+
""")
|
50 |
+
st.latex(r'''\|\nabla f(x) - \nabla f(y)\| \le L\|y - x\|''')
|
51 |
+
|
52 |
+
st.markdown("Please be aware that L-continuous and L-smooth are different concepts! Later on, you might come across some Machine learning terminology that require the function to be L-continuous, or specifically 1-L continuous, we might discuss it more later on.")
|
53 |
+
|
54 |
+
st.markdown("""
|
55 |
+
The L-smooth means the change of the gradient, would never like the absolute function above, change rapidly. It garantee that the gradient of function changes smoothly (as it has both upper bound and lower bound now). More importantly, it garanteed that, if we choose a correct learning rate, we will decrease our loss!
|
56 |
+
|
57 |
+
We are so lucky to choose a function that fit all requirement here! Well, accidently the funciton we choosed was a famous and classic one called **Mean Squared Error (MSE)**.
|
58 |
+
|
59 |
+
Now let's look at detail how to compute the gradient of our loss function. If you are not familiar with calculus, you can just accept the result for now.
|
60 |
+
""")
|
61 |
+
st.latex(r'''L(w) = \frac{1}{l} \sum_{i=1}^{l}(y_i-x_iw)^2''')
|
62 |
+
st.latex(r'''\frac{\partial L}{\partial w} = \frac{2}{l}\sum_{i=1}^{l}x_i(y_i - x_iw) = \frac{2}{l}\sum_{i=1}^{l}x_ie_i''')
|
63 |
+
|
64 |
+
st.markdown("""
|
65 |
+
where $e_i$ is the difference between predicted value and real price in ith row. From the equation above, one can check that, the gradient of MSE is larger when the error is larger, smaller when error is smaller.
|
66 |
+
""")
|
67 |
+
|
68 |
+
st.write("##")
|
69 |
+
|
70 |
+
st.markdown("""
|
71 |
+
|
72 |
+
##### 2.3 Let's actually do the calculation,
|
73 |
+
|
74 |
+
**Step 0**:
|
75 |
+
|
76 |
+
| # Bedrooms | Living area | Sold Price | Myprediction | Error |
|
77 |
+
| ---------- | ----------- | ---------- | ------------ | --- |
|
78 |
+
| 4 | 170 | 1,200,000 | 37000 |-1,163,000 |
|
79 |
+
| 3 | 130 | 980,000 | 28000 |-952,000 |
|
80 |
+
| 5 | 230 | 1,600,000 | 48000 |-1,552,000 |
|
81 |
+
|
82 |
+
- $w_0 = (5000, 100)$
|
83 |
+
- learning rate a = 0.00001$
|
84 |
+
- Calculate""")
|
85 |
+
st.latex(r'''w_1 = w_0 - \alpha \frac{2}{3}\sum_{i=1}^{3}x_ie_i = w_0 -(-101.8, -4522.9) = (5000 + 101.8, 100 + 4522.9) = (5101.8, 4622.9)''')
|
86 |
+
|
87 |
+
st.markdown("""
|
88 |
+
**Step 1**: Calculate new predictions and errors using updated weights, $w_1$
|
89 |
+
|
90 |
+
| # Bedrooms | Living area | Sold Price |Myprediction| Error|
|
91 |
+
| ---------- | ----------- | ---------- | ------------ | --- |
|
92 |
+
| 4 | 170 | 1,200,000 | 806300.2|-393699.8|
|
93 |
+
| 3 | 130 | 980,000 | 616282.4|-363717.6 |
|
94 |
+
| 5 | 230 | 1,600,000 | 1088776|-511224|
|
95 |
+
|
96 |
+
- $w_1 = (5101.8, 4622.9)$
|
97 |
+
- learning rate a = 0.00001
|
98 |
+
- Calculate new weights by:
|
99 |
+
""")
|
100 |
+
st.latex(r'''w_2 = w_1 - \alpha \frac{2}{3}\sum_{i=1}^{3}x_ie_i= w_1 -(-34.8, -1545.3) = (5101.8 + 34.8, 4622.9 + 1545.3) = (5136.6, 6168.2)''')
|
101 |
+
|
102 |
+
st.markdown("""
|
103 |
+
**Step 2**: Calculate new predictions and errors using updated weights, $w_2$, (5136.6, 6168.2)
|
104 |
+
|
105 |
+
| # Bedrooms | Living area | Sold Price |Myprediction| Error|
|
106 |
+
| ---------- | ----------- | ---------- | ------------ | --- |
|
107 |
+
| 4 | 170 | 1,200,000 | 1,069,140.4|-130,859.6|
|
108 |
+
| 3 | 130 | 980,000 | 817,275.8|-162,724.2 |
|
109 |
+
| 5 | 230 | 1,600,000 | 1,444,369|-155,631|
|
110 |
+
|
111 |
+
As you can see from the example above, after 2 iterations, our naive model become much closer to the real price. You might also expect that after more rounds, the error would be smaller. Some reader might also realise the in our example here, the optimal weights can be found explicitly by **:blue[linear regression]**. We are not going to details of linear regression in this article. The real power of Gradient Descent Algorithm is we can build up more conplicated model and train them now.
|
112 |
+
|
113 |
+
""")
|
114 |
+
|
115 |
+
st.write("##")
|
116 |
+
|
117 |
+
st.markdown("""
|
118 |
+
In the real life, please don't be panic to do the calculation above! Computer will do the job for you. I am writing them here just for demonstration, and I would not garantee my accuracy of each number, I trust computer much more than my calculation skills.
|
119 |
+
|
120 |
+
From the next chapter, we will moving on to see how we can use the thing we have leant to deal with some real Natural Language Processing problems. We will use a new example from a classic application of NLP, Sentiment Analysis.
|
121 |
+
""")
|
122 |
+
|
123 |
+
with st.expander("Pass the quiz to get to the next section :)"):
|
124 |
+
st.markdown("To access to the next section, you have to finish the little test below: :smile:")
|
125 |
+
st.write("##")
|
126 |
+
st.markdown("Q1. Is the function $ f(x) := 3x^2 $ L-smooth? ")
|
127 |
+
|
128 |
+
q1 = st.radio("", ["A. yes", "B. no"])
|
129 |
+
|
130 |
+
st.write("##")
|
131 |
+
st.markdown("Q2. With the context from the Q1, what is the mininum L for f(x) to be L-smooth? ")
|
132 |
+
|
133 |
+
q2 = st.radio("", ["A. 2", "B. 4", "C. 6", "D, 8"])
|
134 |
+
|
135 |
+
if st.button("Try your luck..."):
|
136 |
+
if q1 == "A. yes" and q2 == "C. 6":
|
137 |
+
st.write("Congratulations! The password for the next section is smoothness. ")
|
138 |
+
else:
|
139 |
+
st.write("Oop! Unlucky... You may try again!")
|
140 |
+
|
141 |
+
|
142 |
+
|
143 |
+
|
requirements.txt
CHANGED
@@ -1 +1,2 @@
|
|
1 |
-
streamlit==1.24.0
|
|
|
|
1 |
+
streamlit==1.24.0
|
2 |
+
streamlit_book==0.7.5
|
tmp/users.csv
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
user_id,token,datetime
|
2 |
+
0,abc123,2023-07-15 12:35:29
|
3 |
+
1,tte831,2023-07-15 12:35:29
|
4 |
+
2,dcg447,2023-07-15 12:37:20
|
5 |
+
3,kbb530,2023-07-15 12:53:27
|
6 |
+
4,kdt233,2023-07-15 13:34:12
|
7 |
+
5,epo188,2023-07-15 22:25:58
|
8 |
+
6,xhz259,2023-07-16 10:46:21
|
9 |
+
7,cxz229,2023-07-17 13:22:47
|
10 |
+
8,xny887,2023-07-17 14:07:05
|
utils.py
CHANGED
@@ -1,26 +1,26 @@
|
|
1 |
import streamlit as st
|
2 |
|
3 |
-
def check_password():
|
4 |
"""Returns `True` if the user had the correct password."""
|
5 |
|
6 |
def password_entered():
|
7 |
"""Checks whether a password entered by the user is correct."""
|
8 |
-
if st.session_state[
|
9 |
st.session_state["password_correct"] = True
|
10 |
-
del st.session_state[
|
11 |
else:
|
12 |
st.session_state["password_correct"] = False
|
13 |
|
14 |
if "password_correct" not in st.session_state:
|
15 |
# First run, show input for password.
|
16 |
st.text_input(
|
17 |
-
"Password", type="password", on_change=password_entered, key=
|
18 |
)
|
19 |
return False
|
20 |
elif not st.session_state["password_correct"]:
|
21 |
# Password not correct, show input + error.
|
22 |
st.text_input(
|
23 |
-
"Password", type="password", on_change=password_entered, key=
|
24 |
)
|
25 |
st.error("😕 Password incorrect")
|
26 |
return False
|
|
|
1 |
import streamlit as st
|
2 |
|
3 |
+
def check_password(key):
|
4 |
"""Returns `True` if the user had the correct password."""
|
5 |
|
6 |
def password_entered():
|
7 |
"""Checks whether a password entered by the user is correct."""
|
8 |
+
if st.session_state[key] == st.secrets[key]:
|
9 |
st.session_state["password_correct"] = True
|
10 |
+
del st.session_state[key] # don't store password
|
11 |
else:
|
12 |
st.session_state["password_correct"] = False
|
13 |
|
14 |
if "password_correct" not in st.session_state:
|
15 |
# First run, show input for password.
|
16 |
st.text_input(
|
17 |
+
"Password", type="password", on_change=password_entered, key=key
|
18 |
)
|
19 |
return False
|
20 |
elif not st.session_state["password_correct"]:
|
21 |
# Password not correct, show input + error.
|
22 |
st.text_input(
|
23 |
+
"Password", type="password", on_change=password_entered, key=key
|
24 |
)
|
25 |
st.error("😕 Password incorrect")
|
26 |
return False
|