├── 01 FlaskChatbot ├── ChatGPT.py ├── Echo.py ├── GPT3.py ├── Hello.py ├── static │ └── style.css └── templates │ └── index.html ├── 02 BasicNLP └── readme.txt ├── 03 HuggingFace └── huggingface.py ├── 04 BERT ├── BERT_SQuAD_Finetuned.py ├── BERT_for_QA.py ├── SQuAD │ ├── dev-v2.0.json │ └── train-v2.0.json └── squad_feature_creation.py ├── 05 GPT ├── ChatGPT++.py ├── ChatGPT+.py ├── ChatGPT.py ├── GPT+.py └── GPT.py ├── 06 DALLE ├── createPic.py ├── editPic.py ├── mouse_riding_cat.png └── mouse_riding_cat_univ.png ├── 07 Telegram └── OpenAI_Telegram.py └── README.md /01 FlaskChatbot/ChatGPT.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, render_template, request 2 | import openai 3 | 4 | app = Flask(__name__) 5 | 6 | # Set up OpenAI API authentication 7 | openai.api_key = "替换成你的Key" 8 | 9 | @app.route("/") 10 | def home(): 11 | return render_template("index.html") 12 | 13 | @app.route("/get") 14 | def get_bot_response(): 15 | userText = request.args.get('msg') 16 | messages = [] 17 | messages.append({"role":"user","content": userText}) 18 | # Generate response from OpenAI GPT-3 API 19 | response=openai.ChatCompletion.create( 20 | model="gpt-3.5-turbo", 21 | messages=messages 22 | ) 23 | 24 | return str(response["choices"][0]["message"]["content"]) 25 | 26 | if __name__ == "__main__": 27 | app.run() 28 | 29 | 30 | -------------------------------------------------------------------------------- /01 FlaskChatbot/Echo.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, render_template, request 2 | 3 | app = Flask(__name__) 4 | 5 | @app.route("/") 6 | def home(): 7 | return render_template("index.html") 8 | 9 | @app.route("/get") 10 | def get_bot_response(): 11 | userText = request.args.get('msg') 12 | return str(userText) 13 | 14 | if __name__ == "__main__": 15 | app.run() 16 | -------------------------------------------------------------------------------- /01 FlaskChatbot/GPT3.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, render_template, request 2 | import openai 3 | 4 | app = Flask(__name__) 5 | 6 | # Set up OpenAI API authentication 7 | openai.api_key = "替换成你的Key" 8 | 9 | @app.route("/") 10 | def home(): 11 | return render_template("index.html") 12 | 13 | @app.route("/get") 14 | def get_bot_response(): 15 | userText = request.args.get('msg') 16 | 17 | # Generate response from OpenAI GPT-3 API 18 | response = openai.Completion.create( 19 | engine="davinci", 20 | prompt=userText, 21 | max_tokens=60, 22 | n=1, 23 | stop=None, 24 | temperature=0.5, 25 | ) 26 | 27 | return str(response.choices[0].text.strip()) 28 | 29 | if __name__ == "__main__": 30 | app.run() 31 | -------------------------------------------------------------------------------- /01 FlaskChatbot/Hello.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, render_template, request 2 | 3 | app = Flask(__name__) 4 | @app.route("/") 5 | def home(): 6 | return render_template("index.html") 7 | 8 | @app.route("/get") 9 | def get_bot_response(): 10 | userText = request.args.get('msg') 11 | return str("Hello") 12 | 13 | if __name__ == "__main__": 14 | app.run() 15 | -------------------------------------------------------------------------------- /01 FlaskChatbot/static/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Garamond; 3 | } 4 | 5 | h1 { 6 | color: black; 7 | margin-bottom: 0; 8 | margin-top: 0; 9 | text-align: center; 10 | font-size: 40px; 11 | } 12 | 13 | h3 { 14 | color: black; 15 | font-size: 20px; 16 | margin-top: 3px; 17 | text-align: center; 18 | } 19 | 20 | #chatbox { 21 | margin-left: auto; 22 | margin-right: auto; 23 | width: 40%; 24 | margin-top: 60px; 25 | } 26 | 27 | #userInput { 28 | margin-left: auto; 29 | margin-right: auto; 30 | width: 40%; 31 | margin-top: 60px; 32 | } 33 | 34 | #textInput { 35 | width: 87%; 36 | border: none; 37 | border-bottom: 3px solid #009688; 38 | font-family: monospace; 39 | font-size: 17px; 40 | } 41 | 42 | #buttonInput { 43 | padding: 3px; 44 | font-family: monospace; 45 | font-size: 17px; 46 | } 47 | 48 | .userText { 49 | color: white; 50 | font-family: monospace; 51 | font-size: 17px; 52 | text-align: right; 53 | line-height: 30px; 54 | } 55 | 56 | .userText span { 57 | background-color: #009688; 58 | padding: 10px; 59 | border-radius: 2px; 60 | } 61 | 62 | .botText { 63 | color: white; 64 | font-family: monospace; 65 | font-size: 17px; 66 | text-align: left; 67 | line-height: 30px; 68 | } 69 | 70 | .botText span { 71 | background-color: #EF5350; 72 | padding: 10px; 73 | border-radius: 2px; 74 | } 75 | 76 | #tidbit { 77 | position:absolute; 78 | bottom:0; 79 | right:0; 80 | width: 300px; 81 | } -------------------------------------------------------------------------------- /01 FlaskChatbot/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |

Chatbot网页版

9 |

创建你自己的ChatGPT!!

10 |
11 |
12 |

Hi! I'm Chatbot.

13 |
14 |
15 | 16 | 17 |
18 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /02 BasicNLP/readme.txt: -------------------------------------------------------------------------------- 1 | 本节课无代码 -------------------------------------------------------------------------------- /03 HuggingFace/huggingface.py: -------------------------------------------------------------------------------- 1 | # 导入必要的库 2 | from transformers import AutoTokenizer, AutoModelForSequenceClassification 3 | from datasets import load_dataset 4 | 5 | # 定义数据集名称和任务类型 6 | dataset_name = "imdb" 7 | task = "sentiment-analysis" 8 | 9 | # 下载数据集并打乱数据 10 | dataset = load_dataset(dataset_name) 11 | dataset = dataset.shuffle() 12 | 13 | # 初始化分词器和模型 14 | model_name = "bert-base-cased" 15 | tokenizer = AutoTokenizer.from_pretrained(model_name) 16 | model = AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=2) 17 | 18 | # 将文本编码为模型期望的张量格式 19 | inputs = tokenizer(dataset["train"]["text"][:10], padding=True, truncation=True, return_tensors="pt") 20 | 21 | # 将编码后的张量输入模型进行预测 22 | outputs = model(**inputs) 23 | 24 | # 获取预测结果和标签 25 | predictions = outputs.logits.argmax(dim=-1) 26 | labels = dataset["train"]["label"][:10] 27 | 28 | # 打印预测结果和标签 29 | for i, (prediction, label) in enumerate(zip(predictions, labels)): 30 | prediction_label = "正面评论" if prediction == 1 else "负面评论" 31 | true_label = "正面评论" if label == 1 else "负面评论" 32 | print(f"Example {i+1}: Prediction: {prediction_label}, True label: {true_label}") -------------------------------------------------------------------------------- /04 BERT/BERT_SQuAD_Finetuned.py: -------------------------------------------------------------------------------- 1 | from transformers import BertForQuestionAnswering, BertTokenizer, BertForQuestionAnswering, AdamW 2 | import torch 3 | from torch.utils.data import TensorDataset 4 | 5 | # 是否有GPU 6 | device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') 7 | 8 | # 下载未经微调的BERT 9 | tokenizer = BertTokenizer.from_pretrained('bert-base-uncased') 10 | model = BertForQuestionAnswering.from_pretrained('bert-base-uncased').to(device) 11 | 12 | # 评估未经微调的BERT的性能 13 | def china_capital(): 14 | question, text = "What is the capital of China?", "The capital of China is Beijing." 15 | inputs = tokenizer.encode_plus(question, text, add_special_tokens=True, return_tensors="pt") 16 | with torch.no_grad(): 17 | outputs = model(**inputs.to(device)) 18 | answer_start_index = torch.argmax(outputs.start_logits) 19 | answer_end_index = torch.argmax(outputs.end_logits) + 1 20 | predict_answer_tokens = inputs['input_ids'][0][answer_start_index:answer_end_index] 21 | predicted_answer = tokenizer.decode(predict_answer_tokens) 22 | print("中国的首都是?", predicted_answer) 23 | china_capital() 24 | 25 | from transformers import BertTokenizer, BertForQuestionAnswering, AdamW 26 | from torch.utils.data import DataLoader, RandomSampler, SequentialSampler 27 | from transformers.data.processors.squad import SquadV2Processor, SquadExample, squad_convert_examples_to_features 28 | 29 | # 加载SQuAD 2.0数据集的特征 30 | import pickle 31 | with open('04 BERT/SQuAD/train_features.pkl', 'rb') as f: 32 | train_features = pickle.load(f) 33 | 34 | # 定义训练参数 35 | train_batch_size = 8 36 | num_epochs = 3 37 | learning_rate = 3e-5 38 | 39 | # 将特征转换为PyTorch张量 40 | all_input_ids = torch.tensor([f.input_ids for f in train_features], dtype=torch.long) 41 | all_attention_mask = torch.tensor([f.attention_mask for f in train_features], dtype=torch.long) 42 | all_token_type_ids = torch.tensor([f.token_type_ids for f in train_features], dtype=torch.long) 43 | all_start_positions = torch.tensor([f.start_position for f in train_features], dtype=torch.long) 44 | all_end_positions = torch.tensor([f.end_position for f in train_features], dtype=torch.long) 45 | 46 | train_dataset = TensorDataset(all_input_ids, all_attention_mask, all_token_type_ids, all_start_positions, all_end_positions) 47 | num_samples = 100 48 | train_dataset = TensorDataset( 49 | all_input_ids[:num_samples], 50 | all_attention_mask[:num_samples], 51 | all_token_type_ids[:num_samples], 52 | all_start_positions[:num_samples], 53 | all_end_positions[:num_samples]) 54 | train_sampler = RandomSampler(train_dataset) 55 | train_dataloader = DataLoader(train_dataset, sampler=train_sampler, batch_size=train_batch_size) 56 | 57 | # 加载BERT模型和优化器 58 | model = BertForQuestionAnswering.from_pretrained('bert-base-uncased').to(device) 59 | optimizer = AdamW(model.parameters(), lr=5e-5) 60 | 61 | # 微调BERT 62 | for epoch in range(num_epochs): 63 | for step, batch in enumerate(train_dataloader): 64 | model.train() 65 | optimizer.zero_grad() 66 | input_ids, attention_mask, token_type_ids, start_positions, end_positions = tuple(t.to(device) for t in batch) 67 | outputs = model(input_ids=input_ids, 68 | attention_mask=attention_mask, 69 | token_type_ids=token_type_ids, 70 | start_positions=start_positions, 71 | end_positions=end_positions) 72 | loss = outputs.loss 73 | loss.backward() 74 | optimizer.step() 75 | 76 | # Print the training loss every 500 steps 77 | if step % 5 == 0: 78 | print(f"Epoch [{epoch+1}/{num_epochs}], Step [{step+1}/{len(train_dataloader)}], Loss: {loss.item():.4f}") 79 | 80 | china_capital() 81 | 82 | # 保存微调后的模型 83 | model.save_pretrained("04 BERT/SQuAD/SQuAD_finetuned_bert") -------------------------------------------------------------------------------- /04 BERT/BERT_for_QA.py: -------------------------------------------------------------------------------- 1 | from transformers import BertTokenizer, BertForQuestionAnswering 2 | import torch 3 | import numpy as np 4 | 5 | # Set the random seed for PyTorch and NumPy 6 | torch.manual_seed(0) 7 | np.random.seed(0) 8 | 9 | tokenizer = BertTokenizer.from_pretrained('bert-base-uncased') 10 | model = BertForQuestionAnswering.from_pretrained('bert-base-uncased') 11 | 12 | question, text = "What is the capital of China?", "The capital of China is Beijing." 13 | 14 | inputs = tokenizer.encode_plus(question, text, add_special_tokens=True, return_tensors="pt") 15 | with torch.no_grad(): 16 | outputs = model(**inputs) 17 | 18 | answer_start_index = torch.argmax(outputs.start_logits) 19 | answer_end_index = torch.argmax(outputs.end_logits) + 1 20 | 21 | predict_answer_tokens = inputs['input_ids'][0][answer_start_index:answer_end_index] 22 | predicted_answer = tokenizer.decode(predict_answer_tokens) 23 | 24 | print("中国的首都是?", predicted_answer) -------------------------------------------------------------------------------- /04 BERT/squad_feature_creation.py: -------------------------------------------------------------------------------- 1 | import pickle 2 | from transformers.data.processors.squad import SquadV2Processor, squad_convert_examples_to_features 3 | from transformers import BertTokenizer 4 | 5 | # 初始化SQuAD Processor, 数据集, 和分词器 6 | processor = SquadV2Processor() 7 | train_examples = processor.get_train_examples('04 BERT/SQuAD') 8 | tokenizer = BertTokenizer.from_pretrained('bert-base-uncased') 9 | 10 | # 将SQuAD 2.0示例转换为BERT输入特征 11 | train_features = squad_convert_examples_to_features( 12 | examples=train_examples, 13 | tokenizer=tokenizer, 14 | max_seq_length=384, 15 | doc_stride=128, 16 | max_query_length=64, 17 | is_training=True, 18 | return_dataset=False, 19 | threads=1 20 | ) 21 | 22 | # 将特征保存到磁盘上 23 | with open('04 BERT/train_features.pkl', 'wb') as f: 24 | pickle.dump(train_features, f) -------------------------------------------------------------------------------- /05 GPT/ChatGPT++.py: -------------------------------------------------------------------------------- 1 | import openai 2 | 3 | openai.api_key = "替换成你的Key" 4 | messages = [] 5 | print("您好,我们终于见面了!!您希望接下来我为您提供什么服务?") 6 | system_message = input("人类说:") 7 | messages.append({"role":"system","content":system_message}) 8 | print("\n") 9 | 10 | print("好的,明白了! 我会服务好您的。" + "\n" + "现在请和我聊天吧!" + "\n" + "记住,烦我的时候,请说‘再见’") 11 | 12 | while True: 13 | # Collect the user's message 14 | print("\n") 15 | message = input("人类说:") 16 | messages.append({"role":"user","content": message}) 17 | 18 | response=openai.ChatCompletion.create( 19 | model="gpt-3.5-turbo", 20 | messages=messages 21 | ) 22 | 23 | reply = response["choices"][0]["message"]["content"] 24 | print("ChatGPT说: ", reply) 25 | 26 | # Check if the user wants to exit the conversation 27 | if message.lower() == "再见": 28 | break -------------------------------------------------------------------------------- /05 GPT/ChatGPT+.py: -------------------------------------------------------------------------------- 1 | import openai 2 | 3 | openai.api_key="替换成你的Key" 4 | messages = [] 5 | system_message = input("您好,我们终于见面了!!您希望接下来我为您提供什么服务?") 6 | messages.append({"role":"system","content":system_message}) 7 | print("\n") 8 | 9 | print("好的,明白了! 我会服务好您的。" + "\n" + "现在请和我聊天吧!") 10 | 11 | print("\n") 12 | message = input("人类说:") 13 | messages.append({"role":"user","content": message}) 14 | 15 | response=openai.ChatCompletion.create( 16 | model="gpt-3.5-turbo", 17 | messages=messages 18 | ) 19 | 20 | reply = response["choices"][0]["message"]["content"] 21 | print("ChatGPT说: ", reply) -------------------------------------------------------------------------------- /05 GPT/ChatGPT.py: -------------------------------------------------------------------------------- 1 | # Note: you need to be using OpenAI Python v0.27.0 for the code below to work 2 | import openai 3 | 4 | # Set up OpenAI API credentials 5 | openai.api_key = "替换成你的Key" 6 | 7 | response = openai.ChatCompletion.create( 8 | model="gpt-3.5-turbo", 9 | messages=[ 10 | {"role": "system", "content": "You are a helpful assistant."}, 11 | {"role": "user", "content": "Who won the world series in 2020?"}, 12 | {"role": "assistant", "content": "The Los Angeles Dodgers won the World Series in 2020."}, 13 | {"role": "user", "content": "Where was it played?"} 14 | ] 15 | ) 16 | 17 | # Extract the response from the API output 18 | response_text = response.response['choices'][0]['message']['content'] 19 | 20 | # Print the response 21 | print(response_text) -------------------------------------------------------------------------------- /05 GPT/GPT+.py: -------------------------------------------------------------------------------- 1 | import openai 2 | openai.api_key = "替换成你的Key" 3 | 4 | # 调用 ChatGPT API 的函数 5 | def ask_gpt(prompt, model, temperature=0.5): 6 | response = openai.Completion.create( 7 | engine=model, 8 | prompt=prompt, 9 | temperature=temperature, 10 | max_tokens=1024, 11 | n=1, 12 | stop=None, 13 | frequency_penalty=0, 14 | presence_penalty=0 15 | ) 16 | answer = response.choices[0].text.strip() 17 | return answer 18 | 19 | # 示例使用 ChatGPT 进行对话 20 | model = "text-davinci-003" 21 | # model = "gpt-3.5-turbo" 22 | 23 | # 初始化对话 24 | conversation = ["Hello, how can I help you today?"] 25 | 26 | while True: 27 | # 提示用户输入 28 | user_input = input("You: ") 29 | 30 | # 将用户输入添加到对话历史记录中 31 | conversation.append("User: " + user_input) 32 | 33 | # 将对话历史记录作为模型输入 34 | prompt = "\n".join(conversation) 35 | 36 | # 获取 ChatGPT 模型的回复 37 | response = ask_gpt(prompt, model) 38 | 39 | # 将回复添加到对话历史记录中 40 | conversation.append("AI: " + response) 41 | 42 | # 打印 ChatGPT 模型的回复 43 | print("AI: ", response) -------------------------------------------------------------------------------- /05 GPT/GPT.py: -------------------------------------------------------------------------------- 1 | import openai 2 | openai.api_key = "替换成你的Key" 3 | 4 | def ask_question(prompt, model, temperature=0.5): 5 | response = openai.Completion.create( 6 | engine=model, 7 | prompt=prompt, 8 | temperature=temperature, 9 | max_tokens=1024, 10 | n=1, 11 | stop=None, 12 | frequency_penalty=0, 13 | presence_penalty=0 14 | ) 15 | answer = response.choices[0].text.strip() 16 | return answer 17 | 18 | # 示例使用 GPT-3 进行问答 19 | model = "text-davinci-002" 20 | 21 | # 示例问题 22 | prompt = "What is the capital of China?" 23 | 24 | # 获取答案 25 | answer = ask_question(prompt, model) 26 | 27 | print("Q: ", prompt) 28 | print("A: ", answer) -------------------------------------------------------------------------------- /06 DALLE/createPic.py: -------------------------------------------------------------------------------- 1 | import openai 2 | import urllib.request 3 | 4 | # 设置 OpenAI API 密钥 5 | openai.api_key = "替换成你的Key" 6 | 7 | # 使用 DALL-E API 生成一张图片 8 | response = openai.Image.create( 9 | prompt="一只骑着猫的老鼠,在宇宙中,非常梦幻", 10 | n=1, 11 | size="1024x1024" 12 | ) 13 | image_url = response["data"][0]["url"] 14 | 15 | # 下载图片并保存到当前目录 16 | urllib.request.urlretrieve(image_url, "mouse_riding_cat_univ.png") 17 | 18 | # 打印成功信息 19 | print("图片已成功保存到当前目录!") 20 | -------------------------------------------------------------------------------- /06 DALLE/editPic.py: -------------------------------------------------------------------------------- 1 | import openai 2 | import urllib.request 3 | 4 | # Set the OpenAI API key 5 | openai.api_key = "替换成你的Key" 6 | 7 | # Generate image using the DALL-E API 8 | prompt = "a baby inside the masked area of the image 'armnice_masked.jpg'" 9 | response = openai.Image.create_edit( 10 | image=open("armnice_rgba.png", "rb"), 11 | mask=open("armnice_rgba_masked.png", "rb"), 12 | prompt=prompt, 13 | n=1, 14 | size="1024x1024" 15 | ) 16 | 17 | # Download and display the generated image 18 | image_url = response['data'][0]['url'] 19 | 20 | # 下载图片并保存到当前目录 21 | urllib.request.urlretrieve(image_url, "baby_in_mask.png") 22 | 23 | # 打印成功信息 24 | print("图片已成功保存到当前目录!") -------------------------------------------------------------------------------- /06 DALLE/mouse_riding_cat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangjia2019/chatgpt-briefing/e9e738ae622289e66e51581339154d6f4ae6c07e/06 DALLE/mouse_riding_cat.png -------------------------------------------------------------------------------- /06 DALLE/mouse_riding_cat_univ.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangjia2019/chatgpt-briefing/e9e738ae622289e66e51581339154d6f4ae6c07e/06 DALLE/mouse_riding_cat_univ.png -------------------------------------------------------------------------------- /07 Telegram/OpenAI_Telegram.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import openai 3 | from telegram import Update 4 | from telegram.ext import filters, MessageHandler, ApplicationBuilder, CommandHandler, ContextTypes 5 | 6 | # 设置日志记录 7 | logging.basicConfig( 8 | format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', 9 | level=logging.INFO) 10 | 11 | # 设置 OpenAI API key 12 | openai.api_key = "你的Open AI Key" 13 | 14 | # 定义函数,处理 "/start" 命令 15 | async def start(update: Update, context: ContextTypes.DEFAULT_TYPE): 16 | await context.bot.send_message(chat_id=update.effective_chat.id, text="我是一个机器人,请和我聊天吧!") 17 | 18 | # 定义函数,使用 OpenAI 生成回复 19 | async def generate_response(update: Update, context: ContextTypes.DEFAULT_TYPE): 20 | # 获取用户的消息 21 | message = update.message.text 22 | 23 | # 使用 OpenAI 生成回复 24 | response = openai.Completion.create( 25 | engine="text-davinci-003", 26 | prompt=f"{message}\n", 27 | max_tokens=128, 28 | n=1, 29 | stop=None, 30 | temperature=0.5, 31 | ).choices[0].text 32 | 33 | # 将回复发送给用户 34 | await context.bot.send_message(chat_id=update.effective_chat.id, text=response) 35 | 36 | # 定义函数,处理未知命令 37 | async def unknown(update: Update, context: ContextTypes.DEFAULT_TYPE): 38 | await context.bot.send_message(chat_id=update.effective_chat.id, text="抱歉,我不明白您的命令。") 39 | 40 | if __name__ == '__main__': 41 | # 设置 Telegram 机器人 42 | application = ApplicationBuilder().token('你的Telegram Token').build() 43 | 44 | # 添加 "/start" 命令处理器 45 | start_handler = CommandHandler('start', start) 46 | application.add_handler(start_handler) 47 | 48 | # 添加消息处理器,使用 OpenAI 生成回复 49 | generate_response_handler = MessageHandler(filters.TEXT & (~filters.COMMAND), generate_response) 50 | application.add_handler(generate_response_handler) 51 | 52 | # 添加未知命令处理器 53 | unknown_handler = MessageHandler(filters.COMMAND, unknown) 54 | application.add_handler(unknown_handler) 55 | 56 | # 启动机器人,并等待消息的到来 57 | application.run_polling() 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 极客时间 ChatGPT API实战 公开课 2 | 3 | 黄佳 新加坡科技研究局 AI 资深研究员,[《数据分析咖哥十话》](https://item.jd.com/13335199.html)[《零基础学机器学习》](https://item.jd.com/12763913.html) 作者 4 | 5 | 极客时间专栏: https://time.geekbang.org/column/intro/100085501 6 | 7 | ![Alt Text](https://picx.zhimg.com/v2-0f5216d449d034a62d0f22fa28566f3d_1440w.jpg?source=172ae18b) --------------------------------------------------------------------------------